Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix two buffer overruns reachable from safe code
Issue 1: When `zstd::stream::raw::NoOp::run` computes the length of the slice that it should copy, it does not take `output.pos()` into account. Instead it can write up to offset `output.dst.capacity() + output.pos()`, if the input is long enough. So even someone who uses NoOp exactly as it's intended to be used may trigger a buffer overrun. The minimal fix for this issue is to use `output.dst.capacity() - output.pos()` instead, in the call to `min`. Issue 2: zstd_safe::OutBuffer is documented as having the invariant that "pos <= dst.capacity()". However, it can't reliably enforce that invariant. Because `dst` is a public field, its capacity can be changed without updating `pos`. For example, if `dst` is a `Vec`, someone could resize it to be smaller than `pos` and then call `shrink_to_fit` on it. Additionally, it's possible to entirely replace one `Vec` with another, which may have a smaller capacity. In either case, the invariant would be violated without using any `unsafe` blocks. As a result I believe the zstd_safe::CCtx/DCtx methods which use OutBuffer are currently vulnerable to buffer overruns in the underlying C library even if the caller uses only safe Rust. In addition: `zstd::stream::raw::NoOp::run` contains the only `unsafe` blocks in the `zstd` crate. The first of the three `unsafe` blocks there is only safe if OutBuffer's invariant is preserved. Since its invariant can't currently be assumed even if the callers use only safe code, this is a potential buffer overrun as well. I've audited all uses of the private `OutBuffer::pos` field and I believe they're safe, so all possibly-unsafe accesses pass through the public `fn pos()` accessor. Therefore I've added an assertion there to check that the invariant still holds. I believe this method is called infrequently and not performance-critical.
- Loading branch information