fix(filesystem): avoid write length wrap when clamping - #95
Conversation
Signed-off-by: Nikhil Rama Krishna <nikhilramakrishna3005@users.noreply.github.com>
srpatcha
left a comment
There was a problem hiding this comment.
Correct, and the regression test earns its place.
The wrap is real. p and cap are uint32_t, len is size_t, so p + len
promotes to 64-bit and len = SIZE_MAX with p = 2 gives 1 — not greater than
cap, so the clamp is skipped and memcpy gets an unbounded length.
Comparing remaining capacity instead cannot wrap, and the precondition is
already enforced two lines above:
if (p > n->cap) return -1;
if (len > n->cap - p) len = n->cap - p;so n->cap - p has no underflow to worry about. The existing comment at line 132
already warns that n->cap - p "would wrap (both are unsigned)" if pos <= cap
ever slipped — the guard is what makes your form safe, and it is there.
Verified against unfixed code
I could not verify this on your branch directly: it is based on master, whose
duplicate test_crypto_aes / test_crypto_sha512 targets make CMake fail at
configure time, so ctest reports "No tests were found" and no test can run at
all. That is #92 / #93, not anything in this PR.
Applying your patch onto the repaired base instead — clean apply, both files:
with fix 100% tests passed, 0 tests failed out of 1
without fix test_filesystem (Subprocess aborted)
Reverting only the one-line clamp aborts the process, which is the unbounded
memcpy doing what it does. A test that fails on the unfixed code and passes on
the fixed one is the thing that makes this a regression test rather than a
demonstration.
Discovering the inode capacity by writing an oversized buffer, rather than
reaching for the private BLOCK_SIZE, is the right call — it keeps the test
working if that constant changes, and your comment explains why a
merely-oversized write is not enough to reproduce this.
Same class, elsewhere
This is the third unsigned-overflow-invisible-on-a-64-bit-host defect found in
this repository. #87 is the same shape in the OTA path — bytes_written + len
wrapping so an oversized chunk is accepted — and the config parser had a
package-overflow that only ASan surfaced. Worth a sweep for a + b > limit
against unsigned types generally; the safe form is always a > limit - b with
the precondition checked, exactly as you have it here.
Approving. Needs #82 or #93 to land first for CI here to be able to run its own
tests.
Summary
Fixes an integer wraparound in
eos_fs_write()when clamping a write to the remaining inode capacity.The previous check used
p + len > n->cap. When the file position was non-zero andlenwas nearSIZE_MAX, the addition could wrap, allowing the capacity check to be bypassed and passing the unbounded length tomemcpy().Type of Change
Changes
p + len > n->captolen > n->cap - p, after the existingp > n->capguard.Testing
Focused filesystem test compiled with:
gcc -std=c11 -Wall -Wextra -Werror -DEOS_ENABLE_FILESYSTEM=1 -Iinclude -Iservices/filesystem/include -o /tmp/test_fs tests/test_filesystem.c services/filesystem/src/filesystem.cResult: 14/14 filesystem tests passed
The filesystem tests were also run with AddressSanitizer.
Result: 14/14 passed
The regression test was verified against the original capacity check under AddressSanitizer, where the wrapping length reached
memcpy()instead of being clamped.The
eos_filesystemCMake target forvbox_testalso builds successfully.The full
ctestsuite could not be run because the current upstream test configuration contains unrelated duplicatetest_crypto_aesandtest_crypto_sha512CMake targets.Pre-Submission Checklist
-Wall -Wextra -Werrorfor C)<type>(<scope>): <description>conventionmasterRelated Issues
None.
Screenshots / Logs
Not applicable.
Additional Notes
This change preserves the existing
eos_fs_write()behavior: writes larger than the remaining inode capacity are clamped and the number of bytes actually written is returned.The existing
p > n->capguard ensures thatn->cap - pcannot underflow before it is compared withlen.No public API behavior is changed.