feat(compress): move CCtx stage guard into Rust

Project the private ZSTD_CCtx compression-stage value into the Rust copy
bridge and reject non-init sources before any destination mutation. Keep the
C private context layout and reset/table operations behind the existing
callbacks while preserving the stage error contract.

Test Plan:
- ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040 && make -j1
- ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml
- ulimit -v 41943040 && make -j1 -C tests test-zstream ZSTREAM_TESTTIME=-T2s
- ulimit -v 41943040 && make -j1 -C tests test-fuzzer FUZZERTEST=-T3s FUZZER_FLAGS=--no-big-tests
This commit is contained in:
2026-07-19 22:59:43 +02:00
parent 9ab4982780
commit 46dd25a122
2 changed files with 23 additions and 54 deletions
+19 -38
View File
@@ -1672,7 +1672,6 @@ pub unsafe extern "C" fn ZSTD_rust_copyCCtx(state: *const ZSTD_rust_copyCCtxStat
}
}
type CopyCCtxCheckStageFn = unsafe extern "C" fn(*mut c_void, *const c_void) -> usize;
type CopyCCtxResetFn = unsafe extern "C" fn(
*mut c_void,
*const c_void,
@@ -1729,7 +1728,7 @@ pub struct ZSTD_rust_copyCCtxInternalState {
src_cctx: *const c_void,
f_params: *const ZSTD_frameParameters,
pledged_src_size: u64,
check_stage: Option<CopyCCtxCheckStageFn>,
source_stage: *const c_int,
destination_custom_mem: *mut c_void,
source_custom_mem: *const c_void,
reset: Option<CopyCCtxResetFn>,
@@ -1752,7 +1751,6 @@ pub struct ZSTD_rust_copyCCtxInternalState {
}
const _: () = {
assert!(size_of::<CopyCCtxCheckStageFn>() == size_of::<usize>());
assert!(size_of::<CopyCCtxResetFn>() == size_of::<usize>());
assert!(size_of::<CopyCCtxMarkTablesFn>() == size_of::<usize>());
assert!(size_of::<CopyCCtxCopyTablesFn>() == size_of::<usize>());
@@ -1763,7 +1761,7 @@ const _: () = {
offset_of!(ZSTD_rust_copyCCtxInternalState, pledged_src_size) == 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_copyCCtxInternalState, check_stage)
offset_of!(ZSTD_rust_copyCCtxInternalState, source_stage)
== 3 * size_of::<usize>() + size_of::<u64>()
);
assert!(
@@ -1788,25 +1786,18 @@ pub unsafe extern "C" fn ZSTD_rust_copyCCtxInternal(
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
let (
Some(check_stage),
Some(reset),
Some(mark_tables_dirty),
Some(copy_tables),
Some(mark_tables_clean),
) = (
state.check_stage,
let (Some(reset), Some(mark_tables_dirty), Some(copy_tables), Some(mark_tables_clean)) = (
state.reset,
state.mark_tables_dirty,
state.copy_tables,
state.mark_tables_clean,
)
else {
) else {
return ERROR(ZstdErrorCode::Generic);
};
if state.callback_context.is_null()
|| state.src_cctx.is_null()
|| state.f_params.is_null()
|| state.source_stage.is_null()
|| state.destination_custom_mem.is_null()
|| state.source_custom_mem.is_null()
|| state.destination_window.is_null()
@@ -1825,9 +1816,8 @@ pub unsafe extern "C" fn ZSTD_rust_copyCCtxInternal(
return ERROR(ZstdErrorCode::Generic);
}
let stage_error = unsafe { check_stage(state.callback_context, state.src_cctx) };
if ERR_isError(stage_error) {
return stage_error;
if unsafe { *state.source_stage } != ZSTD_COMPRESSION_STAGE_INIT {
return ERROR(ZstdErrorCode::StageWrong);
}
unsafe {
@@ -13900,7 +13890,6 @@ mod tests {
#[derive(Default)]
struct CopyCCtxInternalTestContext {
events: Vec<&'static str>,
stage_result: usize,
reset_result: usize,
frame_params: ZSTD_frameParameters,
pledged_src_size: u64,
@@ -13913,15 +13902,6 @@ mod tests {
unsafe { &mut *context.cast::<CopyCCtxInternalTestContext>() }
}
unsafe extern "C" fn copy_cctx_internal_test_check_stage(
context: *mut c_void,
_src_cctx: *const c_void,
) -> usize {
let context = unsafe { copy_cctx_internal_test_context(context) };
context.events.push("check");
context.stage_result
}
unsafe extern "C" fn copy_cctx_internal_test_reset(
context: *mut c_void,
_src_cctx: *const c_void,
@@ -13961,6 +13941,7 @@ mod tests {
context: &mut CopyCCtxInternalTestContext,
src_cctx: *const c_void,
f_params: &ZSTD_frameParameters,
source_stage: *const c_int,
destination_custom_mem: *mut c_void,
source_custom_mem: *const c_void,
destination_window: *mut c_void,
@@ -13981,7 +13962,7 @@ mod tests {
src_cctx,
f_params,
pledged_src_size: 123,
check_stage: Some(copy_cctx_internal_test_check_stage),
source_stage,
destination_custom_mem,
source_custom_mem,
reset: Some(copy_cctx_internal_test_reset),
@@ -14012,6 +13993,7 @@ mod tests {
checksumFlag: 1,
noDictIDFlag: 1,
};
let source_stage = ZSTD_COMPRESSION_STAGE_INIT;
let source_custom_mem = [0x11usize, 0x22, 0x33];
let mut destination_custom_mem = [0usize; 3];
let mut source_block_state =
@@ -14051,6 +14033,7 @@ mod tests {
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
(&mut destination_window as *mut ZSTD_rust_copyWindowState).cast(),
@@ -14070,10 +14053,7 @@ mod tests {
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, 0);
assert_eq!(
context.events,
["check", "reset", "dirty", "tables", "clean"]
);
assert_eq!(context.events, ["reset", "dirty", "tables", "clean"]);
assert_eq!(context.frame_params, f_params);
assert_eq!(context.pledged_src_size, 123);
assert_eq!(context.zbuff, 7);
@@ -14096,11 +14076,9 @@ mod tests {
#[test]
fn copy_cctx_internal_rejects_stage_before_mutation() {
let mut context = CopyCCtxInternalTestContext {
stage_result: ERROR(ZstdErrorCode::StageWrong),
..CopyCCtxInternalTestContext::default()
};
let mut context = CopyCCtxInternalTestContext::default();
let f_params = ZSTD_frameParameters::default();
let source_stage = ZSTD_COMPRESSION_STAGE_ONGOING;
let source_custom_mem = [0usize; 3];
let mut destination_custom_mem = [0usize; 3];
let mut destination_block_state =
@@ -14117,6 +14095,7 @@ mod tests {
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
ptr::dangling_mut(),
@@ -14136,7 +14115,7 @@ mod tests {
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(context.events, ["check"]);
assert!(context.events.is_empty());
}
#[test]
@@ -14146,6 +14125,7 @@ mod tests {
..CopyCCtxInternalTestContext::default()
};
let f_params = ZSTD_frameParameters::default();
let source_stage = ZSTD_COMPRESSION_STAGE_INIT;
let source_custom_mem = [0usize; 3];
let mut destination_custom_mem = [0usize; 3];
let mut destination_block_state =
@@ -14162,6 +14142,7 @@ mod tests {
&mut context,
0x5000usize as *const c_void,
&f_params,
&source_stage,
(&mut destination_custom_mem as *mut [usize; 3]).cast(),
(&source_custom_mem as *const [usize; 3]).cast(),
ptr::dangling_mut(),
@@ -14181,7 +14162,7 @@ mod tests {
let result = unsafe { ZSTD_rust_copyCCtxInternal(&state) };
assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation));
assert_eq!(context.events, ["check", "reset"]);
assert_eq!(context.events, ["reset"]);
}
#[derive(Default)]