Skip to content

Fix preopen path traversal in WASI 0.2 descriptor.open-at - #466

Open
Luca388 wants to merge 1 commit into
Samsung:mainfrom
Luca388:issue-458-repro
Open

Fix preopen path traversal in WASI 0.2 descriptor.open-at#466
Luca388 wants to merge 1 commit into
Samsung:mainfrom
Luca388:issue-458-repro

Conversation

@Luca388

@Luca388 Luca388 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fixes a path traversal issue in WASI 0.2 descriptor.open-at.

Fix

Prevents .. and symbolic links from escaping the preopen boundary.

Note

The Unix implementation uses openat() and readlinkat() and is still being completed.

Windows support will be implemented separately.

Progress

Completed

  • Reject absolute paths and prevent .. from escaping the preopen root
  • Traverse path components relative to directory file descriptors with openat()
  • Resolve relative symbolic links safely with readlinkat()
  • Reject absolute symbolic-link targets and prevent multi-level symlink escapes
  • Limit symbolic-link resolution depth
  • Clean up opened file descriptors on failure paths
  • Complete the Unix implementation

Remaining

  • Fix final . / .. handling and trailing-slash semantics
  • Implement Windows support

@Luca388
Luca388 force-pushed the issue-458-repro branch 3 times, most recently from fc087c0 to ed4e84f Compare August 4, 2026 16:51
@clover2123 clover2123 linked an issue Aug 5, 2026 that may be closed by this pull request
Comment thread src/wasi/WASI02Impl.cpp Outdated
return false;
}

std::vector<std::string> parsedComponents;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to duplicate these strings? Could we use substrings for this purpose?

Comment thread src/wasi/WASI02Impl.cpp Outdated
path.append(componentPath);

// uv_fs_t req;
// int descriptor = uv_fs_open(NULL, &req, path.c_str(), openFlags, 0666, NULL);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure about changing these functions. We use libuv and uvwasi for maximum portability between platforms as it supports both windows and linux. Dealing directly with system calls might introduce lots of things to maintain.

I think only implementing correct path resolution would be sufficient enough.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that libuv doesn’t provide an openat()-style API for resolving paths relative to a directory FD.

Following your suggestion, I could use uv_fs_realpath() to resolve the path, verify that it remains within the preopen directory, and then open it with uv_fs_open(). However, I think this approach could introduce a TOCTOU vulnerability if the path or a symbolic link changes between the resolution and open operations.

Would this limitation be acceptable here, or is there another mechanism in uvwasi that should be used to avoid it?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wasi 0.1 implementation uses this code:
https://github.com/nodejs/uvwasi/blob/main/src/path_resolver.c#L417

It is partly custom, partly uw. Would be good if we could use only one code path, but this might be too difficult.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I will try reusing uvwasi__resolve_path() by resolving the WASI 0.2 guest path one component at a time, then passing the final resolved path to uv_fs_open().

Since uvwasi__resolve_path() is an internal uvwasi API,, would it be acceptable to include it in WASI.cpp, for example:

extern "C" {
#include "../../third_party/uvwasi/uvwasi/src/path_resolver.h"
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it should work in theory. @ksh8281 what do you think? Is this kind of include acceptable?

@Luca388
Luca388 force-pushed the issue-458-repro branch 3 times, most recently from 7d09bbb to d3def34 Compare August 13, 2026 10:46
@Luca388

Luca388 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

I updated CMake so that the uvwasi internal headers can be included without using relative include paths.
In fileSystemDescriptorOpenAt02, I now use uvwasi__resolve_path() to check the requested path step by step. Symlinks are allowed if they stay inside the preopened directory, but rejected if they point outside it. The resolved path is then passed to uv_fs_open().

@Luca388
Luca388 force-pushed the issue-458-repro branch 2 times, most recently from 818cfbd to f518131 Compare August 13, 2026 11:49
@Luca388
Luca388 marked this pull request as ready for review August 13, 2026 12:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WASI 0.2: open-at allows paths to escape preopened directories

3 participants