Stream: git-wasmtime

Topic: wasmtime / issue #14380 Wasmtime doesn't stat paths-to-sy...


view this post on Zulip Wasmtime GitHub notifications bot (Sep 22 2026 at 20:19):

alexcrichton opened issue #14380:

To the extent that wasi:filesystem's semantics are largely downstream of POSIX's, this setup:

$ mkdir -p /tmp/t/dir && ln -s dir /tmp/t/sym_dir

shows this result on native:

$ python3 -c "import os, stat; print(stat.S_ISDIR(os.lstat('/tmp/t/sym_dir/').st_mode))"
True

whereas this wasm program:

(module
  (import "wasi_snapshot_preview1" "path_filestat_get"
    (func $path_filestat_get (param i32 i32 i32 i32 i32) (result i32)))
  (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
  (memory (export "memory") 1)
  (data (i32.const 100) "sym_dir/")

  (func (export "_start")
    (local $errno i32)
    (local.set $errno
      (call $path_filestat_get
        (i32.const 3)    ;; fd: first preopen
        (i32.const 0)    ;; path_flags: symlink_follow NOT set
        (i32.const 100)  ;; path
        (i32.const 8)    ;; path_len
        (i32.const 256)  ;; filestat out
      ))
    (if (i32.ne (local.get $errno) (i32.const 0))
      (then (call $proc_exit (local.get $errno))))
    ;; exit with `filetype` (offset 16 of `filestat`): 3 = directory
    (call $proc_exit (i32.load8_u (i32.const 272)))
  )
)

yields

$ wasmtime run --dir /tmp/t::/ repro.wat; echo $?
3                       # Linux >= 5.6: openat2 fast path, correct

$ ./no_openat2 wasmtime run --dir /tmp/t::/ repro.wat; echo $?
54                      # everywhere else: ENOTDIR

where no_openat2 is this program:

<details>

#include <errno.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#ifndef __NR_openat2
#define __NR_openat2 437
#endif

int main(int argc, char **argv) {
  struct sock_filter filter[] = {
      /* load syscall number */
      BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
      /* if nr == __NR_openat2 -> return ENOSYS */
      BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat2, 0, 1),
      BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (ENOSYS & SECCOMP_RET_DATA)),
      BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
  };
  struct sock_fprog prog = {
      .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
      .filter = filter,
  };

  if (argc < 2) {
    fprintf(stderr, "usage: %s <command> [args...]\n", argv[0]);
    return 2;
  }
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
    perror("PR_SET_NO_NEW_PRIVS");
    return 2;
  }
  if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog) != 0) {
    perror("seccomp");
    return 2;
  }
  execvp(argv[1], &argv[1]);
  perror("execvp");
  return 2;
}

</details>

Effectively wasmtime isn't stat-ing this path correctly with the same behavior as native when using the manually::open path in filesystem::primitives. An LLM writeup, if useful, is here

view this post on Zulip Wasmtime GitHub notifications bot (Sep 22 2026 at 20:19):

alexcrichton added the wasi:impl label to Issue #14380.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 25 2026 at 07:20):

rvolosatovs closed issue #14380:

To the extent that wasi:filesystem's semantics are largely downstream of POSIX's, this setup:

$ mkdir -p /tmp/t/dir && ln -s dir /tmp/t/sym_dir

shows this result on native:

$ python3 -c "import os, stat; print(stat.S_ISDIR(os.lstat('/tmp/t/sym_dir/').st_mode))"
True

whereas this wasm program:

(module
  (import "wasi_snapshot_preview1" "path_filestat_get"
    (func $path_filestat_get (param i32 i32 i32 i32 i32) (result i32)))
  (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
  (memory (export "memory") 1)
  (data (i32.const 100) "sym_dir/")

  (func (export "_start")
    (local $errno i32)
    (local.set $errno
      (call $path_filestat_get
        (i32.const 3)    ;; fd: first preopen
        (i32.const 0)    ;; path_flags: symlink_follow NOT set
        (i32.const 100)  ;; path
        (i32.const 8)    ;; path_len
        (i32.const 256)  ;; filestat out
      ))
    (if (i32.ne (local.get $errno) (i32.const 0))
      (then (call $proc_exit (local.get $errno))))
    ;; exit with `filetype` (offset 16 of `filestat`): 3 = directory
    (call $proc_exit (i32.load8_u (i32.const 272)))
  )
)

yields

$ wasmtime run --dir /tmp/t::/ repro.wat; echo $?
3                       # Linux >= 5.6: openat2 fast path, correct

$ ./no_openat2 wasmtime run --dir /tmp/t::/ repro.wat; echo $?
54                      # everywhere else: ENOTDIR

where no_openat2 is this program:

<details>

#include <errno.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#ifndef __NR_openat2
#define __NR_openat2 437
#endif

int main(int argc, char **argv) {
  struct sock_filter filter[] = {
      /* load syscall number */
      BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
      /* if nr == __NR_openat2 -> return ENOSYS */
      BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat2, 0, 1),
      BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (ENOSYS & SECCOMP_RET_DATA)),
      BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
  };
  struct sock_fprog prog = {
      .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
      .filter = filter,
  };

  if (argc < 2) {
    fprintf(stderr, "usage: %s <command> [args...]\n", argv[0]);
    return 2;
  }
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
    perror("PR_SET_NO_NEW_PRIVS");
    return 2;
  }
  if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog) != 0) {
    perror("seccomp");
    return 2;
  }
  execvp(argv[1], &argv[1]);
  perror("execvp");
  return 2;
}

</details>

Effectively wasmtime isn't stat-ing this path correctly with the same behavior as native when using the manually::open path in filesystem::primitives. An LLM writeup, if useful, is here


Last updated: Oct 11 2026 at 04:10 UTC