Skip to content

posix.fstatat fails to propogate SYMLINK_NOFOLLOW when linking wasi-libc #20890

@squeek502

Description

@squeek502

Zig Version

0.14.0-dev.653+91c17979f

Steps to Reproduce and Observed Behavior

const std = @import("std");

test "fstatat on a symlink with SYMLINK_NOFOLLOW" {
    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    const testfile = try tmp.dir.createFile("testfile", .{});
    testfile.close();

    try tmp.dir.symLink("testfile", "testsymlink", .{});

    const stat = try std.posix.fstatat(tmp.dir.fd, "testsymlink", std.posix.AT.SYMLINK_NOFOLLOW);

    std.testing.expect(stat.mode & std.posix.S.IFLNK == std.posix.S.IFLNK) catch |err| {
        std.debug.print("stat.mode={X}\n", .{stat.mode});
        return err;
    };
}

This test passes normally and when targeting wasm32-wasi and not linking libc:

$ zig test stat-symlink-test.zig -target wasm32-wasi -femit-bin=stat-symlink-test.wasm --test-no-exec
$ wasmtime --dir=. stat-symlink-test.wasm
All 1 tests passed.

But fails when linking libc (meaning wasi-libc):

$ wasmtime --dir=. stat-symlink-test-libc.wasm
stat.mode=8000
1/1 stat-symlink-test.test.fstatat on a symlink with SYMLINK_NOFOLLOW...FAIL (TestUnexpectedResult)
Unable to dump stack trace: not implemented for Wasm
0 passed; 0 skipped; 1 failed.

(that mode is IFREG aka a file)

This is not a problem with the parameters being passed to fstatat: I confirmed that the AT_SYMLINK_NOFOLLOW is being passed to the fstatat_sym call here:

switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {

But strace shows that the NOFOLLOW flag is being dropped when going through wasmtime (the openat2 call should have O_NOFOLLOW, which it does when not linking wasi-libc):

openat2(13, "testsymlink", {flags=O_RDONLY|O_CLOEXEC|O_PATH, resolve=RESOLVE_NO_MAGICLINKS|RESOLVE_BENEATH}, 24) = 11
statx(11, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=0, ...}) = 0
close(11)                               = 0

My first thought was that this is a wasi-libc/wasmtime bug, similar to #20747, but I have been unable to find a reproduction when building an intended-to-be-equivalent C version via wasi-sdk. Here's the program I'm trying (I'm using wasi-sdk 23.0 and wasmtime 23.0.1):

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    mkdirat(AT_FDCWD, "testdir", 0777);
    int dirfd = openat(AT_FDCWD, "testdir", O_RDONLY|O_DIRECTORY);

    int fd = openat(dirfd, "testfile", O_RDONLY|O_CREAT);
    close(fd);

    symlinkat("testfile", dirfd, "testsymlink");

    struct stat st;
    fstatat(dirfd, "testsymlink", &st, AT_SYMLINK_NOFOLLOW);

    printf("%X\n", st.st_mode);

    if ((st.st_mode & S_IFLNK) == 0) return 1;
    return 0;
}

Built with:

$ WASI_SDK=/home/ryan/Downloads/wasi-sdk-23.0-x86_64-linux
$ $WASI_SDK/bin/clang --sysroot=$WASI_SDK/share/wasi-sysroot stat-symlink.c -o stat-symlink-sdk.wasm

And run with:

$ wasmtime --dir=. stat-symlink-sdk.wasm
A000

(0xA000 is S_IFLNK aka symlink)

Expected Behavior

The test to pass when linking wasi-libc

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavioros-wasiWebAssembly System Interfacestandard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions