fix(restore): keep setuid, setgid and sticky bits when replaying modes - #15
Open
codeAnqiang-ma wants to merge 1 commit into
Open
fix(restore): keep setuid, setgid and sticky bits when replaying modes#15codeAnqiang-ma wants to merge 1 commit into
codeAnqiang-ma wants to merge 1 commit into
Conversation
The shim journals st_mode & 07777, so a chmod entry or a removed directory can carry setuid, setgid or sticky. Replay cast that value straight to os.FileMode, which keeps the permission bits where POSIX has them but encodes those three as flags of its own, and os.Chmod only honours the flags. A 4755 binary came back 0755 and a 1777 directory came back 0777, while the preview printed the recorded mode and the summary counted the entry as restored. Translate the journal value into Go's encoding at the three sites that apply a recorded mode. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14
The bug
The shim journals a mode as
st_mode & 07777(undo_shim.c:816forchmod,undo_shim.c:1134forrmdir), so the recorded value can carry setuid, setgid or sticky. Replay cast that straight to anos.FileMode:os.FileModekeeps the permission bits exactly where POSIX has them, which is why 3-digit modes have always worked, but it encodes setuid, setgid and sticky as flags of its own (ModeSetuid,ModeSetgid,ModeSticky) rather than at POSIX's bits 11/10/9.os.Chmodpasses throughPerm()plus those three flags, so on a cast value it never sees them:os.FileMode(0o4755).Perm()is0o755andos.FileMode(0o4755)&os.ModeSetuidis zero.The result is a silent partial restore. Undoing a
chmodon a setuid binary printsand leaves the file
0755. A sticky directory recorded as1777and recreated by undoing anrmdircomes back0777. The preview promises the recorded mode and the summary counts the entry as restored, so nothing signals that the bits are gone — which matters, because these are exactly the bits whose loss is a security-relevant change rather than a cosmetic one.The journal is intact; only the replay side drops them.
The fix
One converter from the journal's POSIX encoding to Go's, used at the three sites that apply a recorded mode (
MkdirAllandChmodwhen recreating a removed directory, andChmodforchmodentries — the last covers both undo and redo, since they differ only in which field they read).MkdirAll's mode is still subject to the process umask, so the followingos.Chmodis what actually lands the special bits. That was already the shape of the code; it just had nothing to land.Tests
Three tests in
internal/restore:TestModeFromOctalKeepsHighBits— table over the converter, asserting each of the three bits survives andPerm()is untouched.TestRmdirUndoRestoresStickyDirectory— replays a realrmdir <path> 1777entry and stats the result.TestChmodUndoRestoresSetuid— replays a realchmod <path> 4755 755entry and stats the result.The two replay tests fail on
main. Before:Note that the permission assertions in both tests passed even then:
0777and0755were correct, only the high bit was missing. That is the whole bug.After:
gofmt -l .andgo vet ./...printed nothing.This is also why the existing suite never caught it: every mode in the fixtures is three digits,
640/644/600in the e2e cases and755inTestRmdirUndoRecreatesDirectory, and a plain cast is correct for all of those.Not run locally:
make test, i.e.test/e2e.shandtest/hook.sh. I am on macOS, whereshim/undo_shim.cdoes not compile (O_LARGEFILEand other Linux/glibc-isms), somakecannot producelibundo.soand the shim-driven suites cannot start. This change is confined to the Go replay side and touches no shim code or shell hook; CI covers those suites on Linux.If you would like an e2e case alongside these — a 4755 file through the real shim in
test/e2e.sh— say the word and I will add one, though I would not be able to run it locally to confirm it before you do.Found and fixed with AI assistance. I reproduced the bit loss locally, verified every cited line, and reviewed all conclusions before opening this.