Skip to content

Commit 118fe0f

Browse files
committed
export gzvprintf
1 parent 8309f4c commit 118fe0f

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

libz-rs-sys-cdylib/src/gz.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2682,7 +2682,7 @@ unsafe fn gzrewind_help(state: &mut GzState) -> c_int {
26822682
0
26832683
}
26842684

2685-
/// Convert, format, compress, and write the variadic arguments `...` to a file under control of the string format, as in `fprintf`.
2685+
/// Convert, format, compress, and write the variadic arguments `...` to a file under control of the string format, as in `printf`.
26862686
///
26872687
/// # Returns
26882688
///
@@ -2707,7 +2707,23 @@ pub unsafe extern "C-unwind" fn gzprintf(
27072707
unsafe { gzvprintf(file, format, va.as_va_list()) }
27082708
}
27092709

2710+
/// Convert, format, compress, and write the variable argument list to a file under control of the string format, as in `vprintf`.
2711+
///
2712+
/// # Returns
2713+
///
2714+
/// Returns the number of uncompressed bytes actually written, or a negative zlib error code in case of error.
2715+
/// The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to [`gzbuffer`].
2716+
/// The caller should assure that this limit is not exceeded. If it is exceeded, then [`gzvprintf`] will return `0` with nothing written.
2717+
///
2718+
/// Contrary to other implementations that can use the insecure `vsprintf`, the `zlib-rs` library always uses `vsnprintf`,
2719+
/// so attempting to write more bytes than the limit can never run into buffer overflow issues.
2720+
///
2721+
/// # Safety
2722+
///
2723+
/// - The `format` must be a valid C string
2724+
/// - The variadic arguments must correspond with the format string in number and type
27102725
#[cfg(feature = "gzprintf")]
2726+
#[export_name = crate::prefix!(gzvprintf)]
27112727
unsafe extern "C-unwind" fn gzvprintf(
27122728
file: gzFile,
27132729
format: *const c_char,

test-libz-rs-sys/src/gz.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,4 +2189,39 @@ mod gzprintf {
21892189
let _ = read;
21902190
}
21912191
}
2192+
2193+
#[test]
2194+
fn test_gzprintf_buffer_too_large() {
2195+
let _ = crate::assert_eq_rs_ng!({
2196+
// Use a small buffer so we can hit the limit easily.
2197+
let path = "gzprintf-too-large.gz";
2198+
let file = unsafe {
2199+
gzopen(
2200+
CString::new(crate_path(path)).unwrap().as_ptr(),
2201+
CString::new("w").unwrap().as_ptr(),
2202+
)
2203+
};
2204+
assert!(!file.is_null());
2205+
2206+
// Make the buffer just 16 bytes.
2207+
assert_eq!(unsafe { gzbuffer(file, 16) }, 0);
2208+
2209+
// This input does not fit, and hence gzprintf returns 0.
2210+
let too_large = CString::new("1234567890abcdef").unwrap();
2211+
assert_eq!(too_large.as_bytes().len(), 16);
2212+
let written = unsafe { gzprintf(file, too_large.as_ptr()) };
2213+
assert_eq!(written, 0);
2214+
2215+
// Now write something that does fit (15 bytes).
2216+
let fits = CString::new("1234567890abcde").unwrap();
2217+
assert_eq!(fits.as_bytes().len(), 15);
2218+
2219+
let written_ok = unsafe { gzprintf(file, fits.as_ptr()) };
2220+
assert_eq!(written_ok, 15);
2221+
2222+
assert_eq!(unsafe { gzclose(file) }, Z_OK);
2223+
2224+
std::fs::read(crate_path(path)).unwrap()
2225+
});
2226+
}
21922227
}

test-libz-rs-sys/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ macro_rules! assert_eq_rs_ng {
7272

7373
#[allow(unused)]
7474
fn get_crc_table() -> *const [u32; 256];
75+
76+
#[allow(unused)]
77+
fn gzbuffer(file: gzFile, size: core::ffi::c_uint) -> core::ffi::c_int;
78+
79+
#[allow(unused)]
80+
fn gzprintf(
81+
file: gzFile,
82+
format: *const core::ffi::c_char,
83+
va: ...
84+
) -> core::ffi::c_int;
7585
}
7686

7787
$tt

0 commit comments

Comments
 (0)