diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c7e38e550..7a96f3ab4 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2910,6 +2910,8 @@ typedef void (*ZSTD_rust_createCDictAdvancedFreeWorkspace_f)( void* context, void* workspace); typedef void (*ZSTD_rust_createCDictAdvancedFree_f)( void* context, void* cdict); +typedef void (*ZSTD_rust_createCDictAdvancedCopyParams_f)( + void* context, void* destination, const void* source); typedef struct { void* callbackContext; ZSTD_CCtx_params* cctxParams; @@ -2928,6 +2930,8 @@ typedef struct { ZSTD_rust_createCDictAdvancedInit_f init; ZSTD_rust_createCDictAdvancedFreeWorkspace_f freeWorkspace; ZSTD_rust_createCDictAdvancedFree_f free; + const void* originalCctxParams; + ZSTD_rust_createCDictAdvancedCopyParams_f copyParams; } ZSTD_rust_createCDictAdvancedState; void* ZSTD_rust_createCDictAdvanced( const ZSTD_rust_createCDictAdvancedState* state, @@ -2967,8 +2971,12 @@ typedef char ZSTD_rust_create_cdict_advanced_state_layout[ == 13 * sizeof(void*) + 2 * sizeof(U32) && offsetof(ZSTD_rust_createCDictAdvancedState, free) == 14 * sizeof(void*) + 2 * sizeof(U32) + && offsetof(ZSTD_rust_createCDictAdvancedState, originalCctxParams) + == 15 * sizeof(void*) + 2 * sizeof(U32) + && offsetof(ZSTD_rust_createCDictAdvancedState, copyParams) + == 16 * sizeof(void*) + 2 * sizeof(U32) && sizeof(ZSTD_rust_createCDictAdvancedState) - == 15 * sizeof(void*) + 2 * sizeof(U32)) + == 17 * sizeof(void*) + 2 * sizeof(U32)) ? 1 : -1]; typedef void (*ZSTD_rust_createCDictAdvancedWrapperInitParams_f)( @@ -7200,6 +7208,13 @@ static int ZSTD_rust_createCDictAdvanced_validateCustomMem(void* context) return ((!customMem->customAlloc) ^ (!customMem->customFree)) == 0; } +static void ZSTD_rust_createCDictAdvanced_copyParams( + void* context, void* destination, const void* source) +{ + (void)context; + *(ZSTD_CCtx_params*)destination = *(const ZSTD_CCtx_params*)source; +} + static void* ZSTD_rust_createCDictAdvanced_allocate( void* context, size_t workspaceSize) { @@ -7343,9 +7358,6 @@ ZSTD_CDict* ZSTD_createCDict_advanced2( }; DEBUGLOG(3, "ZSTD_createCDict_advanced2, dictSize=%u, mode=%u", (unsigned)dictSize, (unsigned)dictContentType); - if (originalCctxParams == NULL) return NULL; - - cctxParams = *originalCctxParams; context.customMem = customMem; state.callbackContext = &context; state.cctxParams = &cctxParams; @@ -7364,6 +7376,8 @@ ZSTD_CDict* ZSTD_createCDict_advanced2( state.init = ZSTD_rust_createCDictAdvanced_init; state.freeWorkspace = ZSTD_rust_createCDictAdvanced_freeWorkspace; state.free = ZSTD_rust_createCDictAdvanced_free; + state.originalCctxParams = originalCctxParams; + state.copyParams = ZSTD_rust_createCDictAdvanced_copyParams; return (ZSTD_CDict*)ZSTD_rust_createCDictAdvanced( &state, dict, dictSize, (int)dictLoadMethod, (int)dictContentType); diff --git a/rust/src/zstd_compress_dictionary.rs b/rust/src/zstd_compress_dictionary.rs index ac908e690..5131bb051 100644 --- a/rust/src/zstd_compress_dictionary.rs +++ b/rust/src/zstd_compress_dictionary.rs @@ -900,13 +900,16 @@ type CreateCDictAdvancedInitFn = unsafe extern "C" fn( ) -> usize; type CreateCDictAdvancedFreeFn = unsafe extern "C" fn(*mut c_void, *mut c_void); type CreateCDictAdvancedFreeWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void); +type CreateCDictAdvancedCopyParamsFn = + unsafe extern "C" fn(*mut c_void, *mut c_void, *const c_void); /// Explicit projection for the public `ZSTD_createCDict_advanced2` wrapper. /// /// Rust owns context-free parameter preparation, workspace-size policy, -/// custom-memory validation, allocation, and callback ordering. C supplies -/// the private layout-size inputs and retains workspace construction, CDict -/// initialization, and teardown behind narrow callbacks. +/// source-parameter validation/copy ordering, custom-memory validation, +/// allocation, and callback ordering. C supplies the private layout-size +/// inputs and retains workspace construction, CDict initialization, and +/// teardown behind narrow callbacks. /// The pointer fields keep the private `ZSTD_CCtx_params` layout opaque here /// while allowing C to publish the fields selected by the Rust parameter leaf. #[repr(C)] @@ -928,6 +931,8 @@ pub struct ZSTD_rust_createCDictAdvancedState { init: CreateCDictAdvancedInitFn, free_workspace: CreateCDictAdvancedFreeWorkspaceFn, free: CreateCDictAdvancedFreeFn, + original_cctx_params: *const c_void, + copy_params: CreateCDictAdvancedCopyParamsFn, } const _: () = { @@ -1002,9 +1007,17 @@ const _: () = { == size_of::<[usize; 14]>() + size_of::<[u32; 2]>() ); assert!( - size_of::() + offset_of!(ZSTD_rust_createCDictAdvancedState, original_cctx_params) == size_of::<[usize; 15]>() + size_of::<[u32; 2]>() ); + assert!( + offset_of!(ZSTD_rust_createCDictAdvancedState, copy_params) + == size_of::<[usize; 16]>() + size_of::<[u32; 2]>() + ); + assert!( + size_of::() + == size_of::<[usize; 17]>() + size_of::<[u32; 2]>() + ); }; /// Prepare advanced-CDict parameters and run the C-owned construction leaves @@ -1027,10 +1040,22 @@ pub unsafe extern "C" fn ZSTD_rust_createCDictAdvanced( || state.enable_dedicated_dict_search.is_null() || state.use_row_match_finder.is_null() || state.sizing.is_null() + || state.original_cctx_params.is_null() { return ptr::null_mut(); } + // The source is opaque here: C retains the private parameter-layout copy, + // while Rust owns rejecting a missing source and placing that copy before + // parameter preparation and the allocation/initialization callbacks. + unsafe { + (state.copy_params)( + state.callback_context, + state.cctx_params.cast(), + state.original_cctx_params, + ); + } + let prepare_result = unsafe { crate::zstd_compress_params_api::ZSTD_rust_params_prepareAdvancedCDict( state.cctx_params, @@ -4842,11 +4867,24 @@ mod tests { init_dict_load_method: c_int, init_dict_content_type: c_int, init_cctx_params: *const ZSTD_CCtx_params, + copied_destination: *mut c_void, + copied_source: *const c_void, free_workspace: *mut c_void, free_cdict: *mut c_void, init_result: usize, } + unsafe extern "C" fn create_cdict_advanced_test_copy_params( + context: *mut c_void, + destination: *mut c_void, + source: *const c_void, + ) { + let probe = unsafe { &mut *context.cast::() }; + probe.events.push("copy_params"); + probe.copied_destination = destination; + probe.copied_source = source; + } + unsafe extern "C" fn create_cdict_advanced_test_validate_custom_mem( context: *mut c_void, ) -> c_int { @@ -5023,6 +5061,8 @@ mod tests { init: create_cdict_advanced_test_init, free_workspace: create_cdict_advanced_test_free_workspace, free: create_cdict_advanced_test_free, + original_cctx_params: ptr::dangling(), + copy_params: create_cdict_advanced_test_copy_params, } } @@ -5077,6 +5117,7 @@ mod tests { assert_eq!( probe.events, [ + "copy_params", "validate", "allocate", "create_workspace", @@ -5094,6 +5135,8 @@ mod tests { assert_eq!(probe.initialized_cdict, probe.cdict_result); assert_eq!(probe.use_row_match_finder, use_row_match_finder); assert_eq!(probe.init_cdict, probe.cdict_result); + assert_eq!(probe.copied_destination, cctx_params.cast()); + assert_eq!(probe.copied_source, state.original_cctx_params); assert_eq!(probe.init_dict, dict.as_ptr().cast()); assert_eq!(probe.init_dict_size, dict.len()); assert_eq!(probe.init_dict_load_method, ZSTD_DLM_BY_REF); @@ -5124,7 +5167,7 @@ mod tests { }; assert!(result.is_null()); - assert_eq!(probe.events, ["validate"]); + assert_eq!(probe.events, ["copy_params", "validate"]); assert!(probe.created_workspace.is_null()); assert!(probe.free_workspace.is_null()); assert!(probe.free_cdict.is_null()); @@ -5154,7 +5197,7 @@ mod tests { }; assert!(result.is_null()); - assert_eq!(probe.events, ["validate", "allocate"]); + assert_eq!(probe.events, ["copy_params", "validate", "allocate"]); assert_eq!( probe.allocated_workspace_size, create_cdict_advanced_expected_workspace_size( @@ -5170,6 +5213,33 @@ mod tests { assert!(probe.free_cdict.is_null()); } + #[test] + fn create_cdict_advanced_rejects_missing_source_params_before_copy() { + let mut probe = CreateCDictAdvancedProbe::default(); + let mut params_storage = create_cdict_advanced_test_params(); + let cctx_params = params_storage.as_mut_ptr(); + let cparams = ZSTD_compressionParameters::default(); + let enable_dedicated_dict_search = 0; + let use_row_match_finder = 0; + let mut state = create_cdict_advanced_test_state( + &mut probe, + cctx_params, + &cparams, + &enable_dedicated_dict_search, + &use_row_match_finder, + ); + state.original_cctx_params = ptr::null(); + + let result = unsafe { + ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO) + }; + + assert!(result.is_null()); + assert!(probe.events.is_empty()); + assert!(probe.copied_destination.is_null()); + assert!(probe.copied_source.is_null()); + } + #[test] fn create_cdict_advanced_frees_workspace_after_reservation_failure() { let workspace = ptr::dangling_mut::(); @@ -5206,6 +5276,7 @@ mod tests { assert_eq!( probe.events, [ + "copy_params", "validate", "allocate", "create_workspace", @@ -5258,6 +5329,7 @@ mod tests { assert_eq!( probe.events, [ + "copy_params", "validate", "allocate", "create_workspace",