Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sysfs: clean paths lexically before use #2322

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions internal/sysfs/dirfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package sysfs
import (
"io/fs"
"os"
"path"
"strings"

experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
"github.com/tetratelabs/wazero/internal/platform"
Expand All @@ -12,7 +14,7 @@ import (
func DirFS(dir string) experimentalsys.FS {
return &dirFS{
dir: dir,
cleanedDir: ensureTrailingPathSeparator(dir),
cleanedDir: strings.TrimSuffix(dir, "/"),
}
}

Expand Down Expand Up @@ -84,16 +86,21 @@ func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
return utimens(d.join(path), atim, mtim)
}

func (d *dirFS) join(path string) string {
switch path {
case "", ".", "/":
if d.cleanedDir == "/" {
return "/"
func (d *dirFS) join(name string) string {
// Lexically clean `name` to enforce that it always stays within the root
// hierarchy. This is necessary to avoid trivially escaping the root.
// See: https://github.com/tetratelabs/wazero/issues/2321
// TODO: this violates POSIX pathname resolution semantics.
// https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
cleanedName := path.Clean("/" + name)
if cleanedName == "/" {
if d.cleanedDir != "" {
return d.cleanedDir
}
// cleanedDir includes an unnecessary delimiter for the root path.
return d.cleanedDir[:len(d.cleanedDir)-1]
return "/"
}
// TODO: Enforce similar to safefilepath.FromFS(path), but be careful as
// relative path inputs are allowed. e.g. dir or path == ../
return d.cleanedDir + path
if strings.HasSuffix(name, "/") {
return d.cleanedDir + cleanedName + "/"
}
return d.cleanedDir + cleanedName
}
15 changes: 12 additions & 3 deletions internal/sysfs/dirfs_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package sysfs
import (
"io/fs"
"os"
"path"
"strings"

experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
)
Expand Down Expand Up @@ -34,9 +36,16 @@ func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {

// Symlink implements the same method as documented on sys.FS
func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
// Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved
// when dereference the `link` on its usage (e.g. readlink, read, etc).
// https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
// Note: do not resolve `oldName` relative to this dirFS.
// The link result is always resolved on usage (e.g. readlink, read, etc).
// However, this trivially allows escaping the root mount point.
// See: https://github.com/tetratelabs/wazero/issues/2321
// So, lexically clean `oldName`, and enforce that it always points down
// the hierarchy.
oldName = path.Clean(oldName)
if strings.HasPrefix(oldName, "../") || strings.HasPrefix(oldName, "/") {
return experimentalsys.EFAULT
}
err := os.Symlink(oldName, d.join(link))
return experimentalsys.UnwrapOSError(err)
}
2 changes: 1 addition & 1 deletion internal/sysfs/dirfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestDirFS_join(t *testing.T) {
require.Equal(t, ".", testFS.join(""))
require.Equal(t, ".", testFS.join("."))
require.Equal(t, ".", testFS.join("/"))
require.Equal(t, "."+string(os.PathSeparator)+"tmp", testFS.join("tmp"))
require.Equal(t, "./tmp", testFS.join("tmp"))
}

func TestDirFS_String(t *testing.T) {
Expand Down
Loading