Add resource limits for archive extensions and sparse maps - #118
Conversation
|
Please describe your motivation? |
|
Thanks for asking. I have added a Motivation section and opened #119 to separate the problem statement from the implementation. This originated in RustFS's Snowball ingestion path, which streams TAR archives supplied by untrusted network clients. RustFS can enforce logical member limits after The proposed limits are opt-in and remain unlimited by default. The downstream integration is rustfs/rustfs#6942, and the full motivation and compatibility goals are in #119. |
|
Hi @zanieb , could you please review this PR when you have a chance? If everything looks good, I'd appreciate it if you could merge it. Thanks! |
|
I'm pretty hesitant to land any version of this in astral-tokio-tar: the codebase isn't really set up for it, and my experience adding similar limits to tar-codec is that there are a lot of subtleties that are difficult to catch. That combined with the breakage risk to uv itself. @cxymds Have you looked at tar-codec to see if it satisfies your use case? It's a much more constrained parser (no sparse support for example), but it was designed from the ground up to express these kinds of limits. |
|
Thanks for the suggestion. I took a closer look at both The main blockers are that MinIO Snowball archives may end at a complete member boundary without the standard two zero blocks, and that we need access to raw per-member PAX metadata, as well as limits on cumulative extension data and the number of physical headers. The high-level Given the potential regression risk to uv, I agree that we should not pursue #118 in its current form. Would you be open to smaller, focused contributions to |
|
It sounds like you need a lot of non-standard and differential-prone features. I'd suggest rethinking your approach here: astral-tokio-tar is only going to become more strict over time (until we deprecate it entirely), and handling pax records without managing their precedence (which tar-codec does automatically) is a security footgun. In terms of wanting to bypass the two block terminator: tar-codec exposes a member iterator, so you can just prematurely end your iteration rather than letting it fail when the EOF is invalid. I would not recommend that (and it's an error that other libraries let you do it by default), but there's no technical reason why you can't do it. |
|
Thanks, that clarification is helpful. I also phrased the PAX requirement too broadly: RustFS does not need to interpret arbitrary raw PAX records independently. It needs the effective minio.* vendor values after normal PAX precedence has been applied. I agree that this should be built on tar-codec’s precedence-managed state rather than reimplemented downstream. |
Motivation
Server-side consumers may parse TAR archives supplied by untrusted clients. They can bound logical entries after
Archive::entries()yields them, but tokio-tar consumes GNU long-name and long-link payloads, PAX extensions, and GNU sparse continuation data before the corresponding logical entry is exposed. At that point an application-level check is too late to prevent extension buffering, sparse-map growth, or excessive continuation reads. Counting logical entries also does not bound the physical metadata records consumed by the parser.This was encountered while hardening RustFS's MinIO-compatible Snowball ingestion path in rustfs/rustfs#6942. The limits belong at the parser boundary so a caller can reject an archive before the associated allocation or out-of-budget read. They remain opt-in and default to unlimited, preserving behavior for existing callers. The cancellation-safety and stream-fusing changes ensure that an error or cancelled
next()future cannot leave extension parsing in a partially advanced state.The problem statement and compatibility goals are tracked in #119.
Summary
ArchiveBuilderPendingand cancellednext()futuresAll new limits default to unlimited, preserving existing behavior unless a caller opts in.
Testing
cargo fmt --all -- --checkUV_NO_CONFIG=1 uvx --from 'astral-dev-toolchain-cargo-hack>=0.6.45' cargo-hack hack check --all --ignore-private --each-feature --no-dev-depsUV_NO_CONFIG=1 uvx --from 'astral-dev-toolchain-cargo-shear>=1.13.4' cargo-shear shearcargo check --all --all-targets --all-featurescargo testcargo test --no-default-featurescargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo +1.83.0 testCloses #119.