Skip to content

Fix double deorphan caused by relocation mid dir remove #1099

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

geky
Copy link
Member

@geky geky commented May 3, 2025

Long story short: There is a specific case where removing a directory can trigger a deorphan pass, but lfs_remove did not check for this, would try to clean up the (already cleaned) directory orphan, and trigger an assert:

  lfs.c:4890:assert: assert failed with false, expected eq true
      LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);

The specific case being a remove commit that triggers a relocation that creates an orphan.

This is also possible in lfs_rename, but only if you're renaming a directory that implies a remove, which is a pretty rare operation.


This was probably an oversight introduced in the non-recursive commit logic rework (#658).

Fortunately the fix is to just check if we even have an orphan before trying to remove it. We can rely on this instead of the file type, so this fix shouldn't even increase the code size.

I've also significantly increased the state size of the orphan/relocation tests, by splitting them into reentrant and non-reentrant variants. Previously these were artificially limited by the impact to powerloss testing runtime. (Unrelated work introduces TEST_PLS to avoid this tradeoff, so eventually this should be a non-issue.)


Found and root-caused by @Hugh-Baoa
See #1086 for more info

geky added 2 commits May 3, 2025 17:15
These are the same as the related reentrant variants, but by opting out
of powerloss testing, we can test a much larger number of states without
having to worry about the impact on powerloss testing runtime.

Bumped CYCLES from 20 -> 2000.

This reveals an orphan remove bug found by Hugh-Baoa.
Long story short: There is a specific case where removing a directory
can trigger a deorphan pass, but lfs_remove did not check for this,
would try to clean up the (already cleaned) directory orphan, and
trigger an assert:

  lfs.c:4890:assert: assert failed with false, expected eq true
      LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);

The specific case being a remove commit that triggers a relocation that
creates an orphan.

This is also possible in lfs_rename, but only if you're renaming a
directory that implies a remove, which is a pretty rare operation.

---

This was probably an oversight introduced in the non-recursive commit
logic rework.

Fortunately the fix is to just check if we even have an orphan before
trying to remove it. We can rely on this instead of the file type, so
this fix shouldn't even increase the code size.

Found and root-caused by Hugh-Baoa
@geky-bot
Copy link
Collaborator

geky-bot commented May 4, 2025

Tests passed ✓, Code: 17104 B (-0.1%), Stack: 1448 B (+0.0%), Structs: 812 B (+0.0%)
Code Stack Structs Coverage
Default 17104 B (-0.1%) 1448 B (+0.0%) 812 B (+0.0%) Lines 2433/2595 lines (+0.0%)
Readonly 6230 B (+0.0%) 448 B (+0.0%) 812 B (+0.0%) Branches 1283/1616 branches (-0.0%)
Threadsafe 17960 B (-0.0%) 1448 B (+0.0%) 820 B (+0.0%) Benchmarks
Multiversion 17176 B (-0.1%) 1448 B (+0.0%) 816 B (+0.0%) Readed 29369693876 B (+0.0%)
Migrate 18768 B (-0.1%) 1752 B (+0.0%) 816 B (+0.0%) Proged 1482874766 B (+0.0%)
Error-asserts 17916 B (+0.1%) 1440 B (+0.0%) 812 B (+0.0%) Erased 1568888832 B (+0.0%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants