From 6f3c6feae9c54afd037174adf427c447b0ce8694 Mon Sep 17 00:00:00 2001 From: Tristan <1075304+TristanInSec@users.noreply.github.com> Date: Tue, 7 Jul 2026 13:27:01 -0700 Subject: [PATCH] sentry/fuse: validate NodeID in LOOKUP responses The NodeID validation (reject 0 and FUSE_ROOT_ID) in newEntry was gated behind `opcode != linux.FUSE_LOOKUP`, so LOOKUP responses from the FUSE daemon bypassed all NodeID checks. A FUSE daemon could return NodeID=0 (which means "no such entry" in the FUSE protocol) and gVisor would create a real inode with that ID, or return FUSE_ROOT_ID to alias the root inode. This splits the validation so that NodeID checks apply to all response types. For LOOKUP with NodeID=0, returns ENOENT to match Linux `fuse_lookup_name()` behavior for negative lookups. For FUSE_ROOT_ID or NodeID=0 on non-LOOKUP opcodes, returns EIO. File type validation remains LOOKUP-exempt since LOOKUP doesn't pass an expected type. FUTURE_COPYBARA_INTEGRATE_REVIEW=https://github.com/google/gvisor/pull/13525 from TristanInSec:fix-fuse-lookup-nodeid-validation f4f4837060b6e1e2fd18c031588dd8bda88ef73a PiperOrigin-RevId: 944063075 --- pkg/sentry/fsimpl/fuse/inode.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/sentry/fsimpl/fuse/inode.go b/pkg/sentry/fsimpl/fuse/inode.go index f3c233fa2a..fbffdb193a 100644 --- a/pkg/sentry/fsimpl/fuse/inode.go +++ b/pkg/sentry/fsimpl/fuse/inode.go @@ -234,7 +234,16 @@ func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMo if err != nil { return nil, err } - if opcode != linux.FUSE_LOOKUP && ((out.Attr.Mode&linux.S_IFMT)^uint32(fileType) != 0 || out.NodeID == 0 || out.NodeID == linux.FUSE_ROOT_ID) { + if out.NodeID == 0 { + if opcode == linux.FUSE_LOOKUP { + return nil, linuxerr.ENOENT + } + return nil, linuxerr.EIO + } + if out.NodeID == linux.FUSE_ROOT_ID { + return nil, linuxerr.EIO + } + if opcode != linux.FUSE_LOOKUP && (out.Attr.Mode&linux.S_IFMT)^uint32(fileType) != 0 { return nil, linuxerr.EIO } child, err := i.fs.newInode(ctx, out.FUSEEntryOut)