Skip to content

fix: prevent memory limiter bypass via u32 truncation in Archive::total_size - #19

Open
yasinlex wants to merge 1 commit into
genlayerlabs:v0.3.xfrom
yasinlex:fix/archive-total-size-u32-truncation
Open

fix: prevent memory limiter bypass via u32 truncation in Archive::total_size#19
yasinlex wants to merge 1 commit into
genlayerlabs:v0.3.xfrom
yasinlex:fix/archive-total-size-u32-truncation

Conversation

@yasinlex

@yasinlex yasinlex commented Aug 4, 2026

Copy link
Copy Markdown

Problem

Archive::total_size is stored as u32 and set via silent as u32 casts in all three constructors (from_ustar, from_zip, from_file_and_runner). If a runner archive exceeds 4,294,967,295 bytes (~4 GB), the as u32 cast silently truncates the value.

For example, a 4 GB + 1 byte archive becomes total_size == 1. When cache.rs calls limiter.consume(arch.total_size), only 1 byte is charged against the memory budget — the full multi-GB payload is still loaded into the BTreeMap<String, bytes::Bytes>, completely bypassing the memory limiter.

This is a denial-of-service vector: an attacker who can supply a runner archive can cause unbounded memory consumption in the executor.

Fix

  • Change total_size field type from u32 to u64
  • Clamp to u32::MAX at all construction sites using .min(u32::MAX as usize) — archives larger than 4 GB are charged at the maximum, correctly exhausting the remaining budget and returning OOM instead of silently passing
  • Add explicit as u32 casts at limiter.consume() call sites in cache.rs for clarity (the clamped u64 value always fits in u32)

Files changed

  • executor/src/runners/ustar.rs: field type u32→u64, 3 construction sites clamped
  • executor/src/runners/cache.rs: 2 explicit as u32 casts at limiter.consume() call sites

Summary by CodeRabbit

  • Bug Fixes
    • Improved archive size handling to support larger archives reliably.
    • Preserved existing size limits while preventing size-related accounting errors.
    • Maintained existing out-of-memory behavior.

…al_size

Archive::total_size was stored as u32 and set via silent `as u32` casts in all
three constructors (from_ustar, from_zip, from_file_and_runner). Archives
exceeding 4 GB silently wrap to a small value, causing limiter.consume() to
charge only a tiny amount — the full multi-GB payload is still loaded,
bypassing the memory limiter entirely.

- Change total_size field type from u32 to u64
- Clamp to u32::MAX at construction sites (.min(u32::MAX as usize))
  so archives larger than 4 GB are charged at the maximum, correctly
  exhausting the budget and returning OOM instead of silently passing
- Add explicit as u32 casts at limiter.consume() call sites in cache.rs
  for clarity (the clamped u64 value always fits in u32)
@yasinlex
yasinlex changed the base branch from main to v0.3.x August 4, 2026 20:29
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 85f3af27-ab64-4977-bc6d-9153a53fbf24

📥 Commits

Reviewing files that changed from the base of the PR and between acb37c7 and a21d76c.

📒 Files selected for processing (2)
  • executor/src/runners/cache.rs
  • executor/src/runners/ustar.rs

📝 Walkthrough

Walkthrough

Archive size storage now uses capped u64 values. Cache memory limiter calls cast archive sizes to u32. Existing OOM handling remains unchanged.

Changes

Archive Size Accounting

Layer / File(s) Summary
Archive size representation
executor/src/runners/ustar.rs
Archive::total_size now uses u64. Archive constructors cap source, buffer, and file sizes at u32::MAX before conversion.
Cache limiter accounting
executor/src/runners/cache.rs
Cached and newly loaded archive sizes are cast to u32 before memory limiter consumption. OOM handling remains unchanged.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing memory limiter bypass caused by u32 truncation in Archive::total_size.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant