Skip to content

iloop when readdir on dir containing symlink #11652

@wingo

Description

@wingo

Test case

use std::process;
extern crate wit_bindgen;

wit_bindgen::generate!({
    inline: r"
  package test:test;

  world test {
      include wasi:filesystem/imports@0.3.0-rc-2025-08-15;
      include wasi:cli/command@0.3.0-rc-2025-08-15;
  }
",
    additional_derives: [PartialEq, Eq, Hash, Clone],
    // Work around https://github.com/bytecodealliance/wasm-tools/issues/2285.
    features:["clocks-timezone"],
    async: [
        "wasi:cli/run@0.3.0-rc-2025-08-15#run",
    ],
    generate_all
});

async fn test_filesystem() {
    match &wasi::filesystem::preopens::get_directories()[..] {
        [(dir, _)] => {
            match dir.symlink_at("..".to_string(), "parent".to_string()).await {
                Ok(()) => {},
                Err(wasi::filesystem::types::ErrorCode::Exist) => {},
                Err(err) => { panic!("{}", err); }
            }
            let (stream, result) = dir.read_directory().await;
            stream.collect().await;
            result.await.unwrap();
        },
        [..] => {
            eprintln!("usage: run with one open dir");
            process::exit(1)
        }
    }
}

struct Component;
export!(Component);
impl exports::wasi::cli::run::Guest for Component {
    async fn run() -> Result<(), ()> {
        test_filesystem().await;
        Ok(())
    }
}

fn main() {
    unreachable!("main is a stub");
}

Run via wasmtime --dir t -Wcomponent-model-async=y -Sp3=y test.wasm.

Expected behavior

The stream.collect() completes. But it doesn't: it iloops. Here's a snippet from the strace:

statx(11, "parent", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=2, ...}) = 0
getdents64(12, 0x56340a5814e0 /* 0 entries */, 1536) = 0
close(12)                               = 0
close(11)                               = 0
openat(3, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 11
fcntl(11, F_GETFL)                      = 0x38000 (flags O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_DIRECTORY)
openat(11, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 12
getdents64(12, 0x56340a581150 /* 3 entries */, 768) = 80
statx(11, "parent", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=2, ...}) = 0
getdents64(12, 0x56340a5814e0 /* 0 entries */, 1536) = 0
close(12)                               = 0
close(11)                               = 0
openat(3, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 11
fcntl(11, F_GETFL)                      = 0x38000 (flags O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_DIRECTORY)
openat(11, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 12
getdents64(12, 0x56340a581150 /* 3 entries */, 768) = 80
statx(11, "parent", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=2, ...}) = 0
getdents64(12, 0x56340a5814e0 /* 0 entries */, 1536) = 0
close(12)                               = 0
close(11)                               = 0
openat(3, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 11
fcntl(11, F_GETFL)                      = 0x38000 (flags O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_DIRECTORY)
openat(11, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 12
getdents64(12, 0x56340a581150 /* 3 entries */, 768) = 80
statx(11, "parent", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=2, ...}) = 0
getdents64(12, 0x56340a5814e0 /* 0 entries */, 1536) = 0
close(12)                               = 0
close(11)                               = 0
openat(3, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 11
fcntl(11, F_GETFL)                      = 0x38000 (flags O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_DIRECTORY)
openat(11, ".", O_RDONLY|O_LARGEFILE|O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY) = 12
getdents64(12, 0x56340a581150 /* 3 entries */, 768) = 80
statx(11, "parent", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFLNK|0777, stx_size=2, ...}) = 0
getdents64(12, 0x56340a5814e0 /* 0 entries */, 1536) = 0
close(12)                               = 0
close(11)                               = 0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIncorrect behavior in the current implementation that needs fixing

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions