feat(compress): validate stable buffers in Rust
Keep the private ZSTD_CCtx and public buffer structures in C while moving the read-only stable-buffer validation policy behind a scalar ABI. The Rust helper compares expected and current input pointers and positions without dereferencing raw pointers, and uses wrapping subtraction for the stable output remainder. The C wrapper still extracts the context and buffer fields, and the existing compressStream2 FORWARD_IF_ERROR path remains responsible for propagating the encoded stability error. The unused endOp parameter is no longer carried into the validation helper. Test Plan: - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression` -- passed, 309 tests. - All three requested clippy modes passed before and after `cargo +nightly fmt --manifest-path rust/Cargo.toml`. - `make -B -C lib -j2 lib` -- passed. - `make -C tests test-rust-lib-smoke` -- passed. - `tests/fuzzer -s4560 -t56 -i57 -v` -- passed. - `make -C tests -j2 test-zstream` -- passed, 84 named plus 5,333 and 8,072 randomized cases. - `git diff --check` and `git diff --cached --check` -- passed. - `make -C tests -j2 test-zstream32` -- unavailable because the i686 Rust target is not installed.
This commit is contained in:
@@ -105,6 +105,11 @@ ZSTD_inBuffer ZSTD_rust_inBufferForEndFlush(int inBufferMode,
|
|||||||
const void* expectedSrc,
|
const void* expectedSrc,
|
||||||
size_t expectedSize,
|
size_t expectedSize,
|
||||||
size_t expectedPos);
|
size_t expectedPos);
|
||||||
|
size_t ZSTD_rust_checkBufferStability(
|
||||||
|
int inBufferMode, int outBufferMode,
|
||||||
|
const void* expectedInSrc, size_t expectedInPos,
|
||||||
|
const void* inputSrc, size_t inputPos,
|
||||||
|
size_t expectedOutBufferSize, size_t outputSize, size_t outputPos);
|
||||||
|
|
||||||
/* Context-free compression-parameter selection and sizing leaves live in
|
/* Context-free compression-parameter selection and sizing leaves live in
|
||||||
* Rust (rust/src/zstd_compress_params.rs). This file retains
|
* Rust (rust/src/zstd_compress_params.rs). This file retains
|
||||||
@@ -4790,21 +4795,18 @@ ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const
|
|||||||
*/
|
*/
|
||||||
static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
|
static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
|
||||||
ZSTD_outBuffer const* output,
|
ZSTD_outBuffer const* output,
|
||||||
ZSTD_inBuffer const* input,
|
ZSTD_inBuffer const* input)
|
||||||
ZSTD_EndDirective endOp)
|
|
||||||
{
|
{
|
||||||
if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
|
return ZSTD_rust_checkBufferStability(
|
||||||
ZSTD_inBuffer const expect = cctx->expectedInBuffer;
|
(int)cctx->appliedParams.inBufferMode,
|
||||||
if (expect.src != input->src || expect.pos != input->pos)
|
(int)cctx->appliedParams.outBufferMode,
|
||||||
RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableInBuffer enabled but input differs!");
|
cctx->expectedInBuffer.src,
|
||||||
}
|
cctx->expectedInBuffer.pos,
|
||||||
(void)endOp;
|
input->src,
|
||||||
if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
|
input->pos,
|
||||||
size_t const outBufferSize = output->size - output->pos;
|
cctx->expectedOutBufferSize,
|
||||||
if (cctx->expectedOutBufferSize != outBufferSize)
|
output->size,
|
||||||
RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableOutBuffer enabled but output size differs!");
|
output->pos);
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4972,7 +4974,7 @@ size_t ZSTD_compressStream2_c( ZSTD_CCtx* cctx,
|
|||||||
}
|
}
|
||||||
/* end of transparent initialization stage */
|
/* end of transparent initialization stage */
|
||||||
|
|
||||||
FORWARD_IF_ERROR(ZSTD_checkBufferStability(cctx, output, input, endOp), "invalid buffers");
|
FORWARD_IF_ERROR(ZSTD_checkBufferStability(cctx, output, input), "invalid buffers");
|
||||||
/* compression stage */
|
/* compression stage */
|
||||||
#ifdef ZSTD_MULTITHREAD
|
#ifdef ZSTD_MULTITHREAD
|
||||||
if (cctx->appliedParams.nbWorkers > 0) {
|
if (cctx->appliedParams.nbWorkers > 0) {
|
||||||
|
|||||||
@@ -198,6 +198,61 @@ pub extern "C" fn ZSTD_rust_inBufferForEndFlush(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn check_buffer_stability(
|
||||||
|
in_buffer_mode: c_int,
|
||||||
|
out_buffer_mode: c_int,
|
||||||
|
expected_in_src: *const c_void,
|
||||||
|
expected_in_pos: usize,
|
||||||
|
input_src: *const c_void,
|
||||||
|
input_pos: usize,
|
||||||
|
expected_out_buffer_size: usize,
|
||||||
|
output_size: usize,
|
||||||
|
output_pos: usize,
|
||||||
|
) -> usize {
|
||||||
|
if in_buffer_mode == ZSTD_BM_STABLE
|
||||||
|
&& (expected_in_src != input_src || expected_in_pos != input_pos)
|
||||||
|
{
|
||||||
|
return ERROR(ZstdErrorCode::StabilityConditionNotRespected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if out_buffer_mode == ZSTD_BM_STABLE
|
||||||
|
&& expected_out_buffer_size != output_size.wrapping_sub(output_pos)
|
||||||
|
{
|
||||||
|
return ERROR(ZstdErrorCode::StabilityConditionNotRespected);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate the stable input/output buffer expectations without crossing the
|
||||||
|
/// private `ZSTD_CCtx` layout into Rust. Raw pointers are compared only for
|
||||||
|
/// identity and are never dereferenced.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ZSTD_rust_checkBufferStability(
|
||||||
|
in_buffer_mode: c_int,
|
||||||
|
out_buffer_mode: c_int,
|
||||||
|
expected_in_src: *const c_void,
|
||||||
|
expected_in_pos: usize,
|
||||||
|
input_src: *const c_void,
|
||||||
|
input_pos: usize,
|
||||||
|
expected_out_buffer_size: usize,
|
||||||
|
output_size: usize,
|
||||||
|
output_pos: usize,
|
||||||
|
) -> usize {
|
||||||
|
check_buffer_stability(
|
||||||
|
in_buffer_mode,
|
||||||
|
out_buffer_mode,
|
||||||
|
expected_in_src,
|
||||||
|
expected_in_pos,
|
||||||
|
input_src,
|
||||||
|
input_pos,
|
||||||
|
expected_out_buffer_size,
|
||||||
|
output_size,
|
||||||
|
output_pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn select_sequence_copier(mode: c_int) -> c_int {
|
fn select_sequence_copier(mode: c_int) -> c_int {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
@@ -1404,6 +1459,146 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_buffer_stability_accepts_matching_stable_input() {
|
||||||
|
let expected_src = b"input".as_ptr().cast::<c_void>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
check_buffer_stability(
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
0,
|
||||||
|
37,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
0,
|
||||||
|
37,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_buffer_stability_rejects_changed_input_pointer_or_position() {
|
||||||
|
let expected_src = b"input".as_ptr().cast::<c_void>();
|
||||||
|
let other_src = b"other".as_ptr().cast::<c_void>();
|
||||||
|
let error = ERROR(ZstdErrorCode::StabilityConditionNotRespected);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
other_src,
|
||||||
|
11,
|
||||||
|
0,
|
||||||
|
37,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
expected_src,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
37,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_buffer_stability_ignores_input_changes_in_buffered_mode() {
|
||||||
|
let expected_src = b"input".as_ptr().cast::<c_void>();
|
||||||
|
let other_src = b"other".as_ptr().cast::<c_void>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
expected_src,
|
||||||
|
11,
|
||||||
|
other_src,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
37,
|
||||||
|
5,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_buffer_stability_validates_stable_output_remainder() {
|
||||||
|
let expected_src = b"input".as_ptr().cast::<c_void>();
|
||||||
|
let error = ERROR(ZstdErrorCode::StabilityConditionNotRespected);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
expected_src,
|
||||||
|
0,
|
||||||
|
ptr::null(),
|
||||||
|
0,
|
||||||
|
32,
|
||||||
|
40,
|
||||||
|
8,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
expected_src,
|
||||||
|
0,
|
||||||
|
ptr::null(),
|
||||||
|
0,
|
||||||
|
32,
|
||||||
|
40,
|
||||||
|
7,
|
||||||
|
),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ZSTD_rust_checkBufferStability(
|
||||||
|
ZSTD_BM_BUFFERED,
|
||||||
|
ZSTD_BM_STABLE,
|
||||||
|
expected_src,
|
||||||
|
0,
|
||||||
|
ptr::null(),
|
||||||
|
0,
|
||||||
|
usize::MAX,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sequence_copier_selector_returns_no_delimiters_mode() {
|
fn sequence_copier_selector_returns_no_delimiters_mode() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Reference in New Issue
Block a user