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

Lock opened file inodes to prevent a use after free race condition #415

Merged
merged 3 commits into from
Aug 27, 2024
Merged
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
24 changes: 16 additions & 8 deletions fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,13 @@ func (f *Filesystem) Open(cancel <-chan struct{}, in *fuse.OpenIn, out *fuse.Ope

ctx.Debug().Msg("")

// we have something on disk-
// verify content against what we're supposed to have
inode.Lock()
defer inode.Unlock()
// stay locked until end to prevent multiple Opens() from competing for
// downloads of the same file.

// try grabbing from disk
fd, err := f.content.Open(id)
if err != nil {
Expand All @@ -494,19 +501,16 @@ func (f *Filesystem) Open(cancel <-chan struct{}, in *fuse.OpenIn, out *fuse.Ope
return fuse.OK
}

// we have something on disk-
// verify content against what we're supposed to have
inode.Lock()
defer inode.Unlock()
// stay locked until end to prevent multiple Opens() from competing for
// downloads of the same file.

if inode.VerifyChecksum(graph.QuickXORHashStream(fd)) {
// disk content is only used if the checksums match
ctx.Info().Msg("Found content in cache.")

// we check size ourselves in case the API file sizes are WRONG (it happens)
st, _ := fd.Stat()
st, err := fd.Stat()
if err != nil {
ctx.Error().Err(err).Msg("Could not fetch file stats.")
return fuse.EIO
}
inode.DriveItem.Size = uint64(st.Size())
return fuse.OK
}
Expand Down Expand Up @@ -702,6 +706,10 @@ func (f *Filesystem) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Status
Uint64("nodeID", in.NodeId).
Msg("")
f.Fsync(cancel, &fuse.FsyncIn{InHeader: in.InHeader})

// grab a lock to prevent a race condition closing an opened file prior to its use (use after free segfault)
inode.Lock()
defer inode.Unlock()
f.content.Close(id)
return 0
}
Expand Down