From ac82102663f832247daa5c83943907a52c8f6153 Mon Sep 17 00:00:00 2001 From: "linfeng.ouyang" Date: Sun, 4 Jan 2026 15:45:30 +0800 Subject: [PATCH 1/2] fix(encode): adjust max pack size for 32-bit targets to prevent overflow Signed-off-by: linfeng.ouyang --- src/internal/pack/encode.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/internal/pack/encode.rs b/src/internal/pack/encode.rs index 1ffc7f3b..b5c319cb 100644 --- a/src/internal/pack/encode.rs +++ b/src/internal/pack/encode.rs @@ -753,9 +753,17 @@ mod tests { /// Check if the given data is a valid pack file format by attempting to decode it. fn check_format(data: &Vec) { + // Use a smaller cap on 32-bit targets to avoid usize overflow. + let max_pack_size_u64 = if cfg!(target_pointer_width = "64") { + 6u64 * 1024 * 1024 * 1024 + } else { + 2u64 * 1024 * 1024 * 1024 + }; + let max_pack_size = usize::try_from(max_pack_size_u64) + .expect("pack size cap should fit in usize for this target"); let mut p = Pack::new( None, - Some(1024 * 1024 * 1024 * 6), // 6GB + Some(max_pack_size), // 6GB on 64-bit, 2GB on 32-bit Some(PathBuf::from("/tmp/.cache_temp")), true, ); From 2f75678fc1aa1067ce5dcf82f7c2ac6160016f02 Mon Sep 17 00:00:00 2001 From: "linfeng.ouyang" Date: Sun, 4 Jan 2026 16:18:58 +0800 Subject: [PATCH 2/2] fix(decode): 32-bit overflow in pack cache sizing Signed-off-by: linfeng.ouyang --- src/internal/pack/decode.rs | 19 ++++++++++++++++++- src/internal/pack/encode.rs | 9 +++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/internal/pack/decode.rs b/src/internal/pack/decode.rs index 8669dc78..67bb92ff 100644 --- a/src/internal/pack/decode.rs +++ b/src/internal/pack/decode.rs @@ -113,7 +113,10 @@ impl Pack { temp_path.pop(); } let thread_num = thread_num.unwrap_or_else(num_cpus::get); - let cache_mem_size = mem_limit.map(|mem_limit| mem_limit * 4 / 5); + let cache_mem_size = mem_limit.map(|mem_limit| { + // Use wider math to avoid 32-bit overflow when computing 80%. + ((mem_limit as u128) * 4 / 5) as usize + }); Pack { number: 0, signature: ObjectHash::default(), @@ -838,6 +841,20 @@ mod tests { } } + #[test] + #[cfg(target_pointer_width = "32")] + fn test_pack_new_mem_limit_no_overflow_32bit() { + // In the old code, 1.2B * 4 produced an intermediate 4.8B value, which exceeds + // 32-bit usize::MAX (~4.29B) and overflowed before a later division; this test + // covers that former panic path. + let mem_limit = 1_200_000_000usize; + let tmp = PathBuf::from("/tmp/.cache_temp"); + let result = std::panic::catch_unwind(|| { + let _p = Pack::new(Some(1), Some(mem_limit), Some(tmp), true); + }); + assert!(result.is_ok(), "Pack::new should not panic on 32-bit"); + } + /// Helper function to run decode tests without delta objects fn run_decode_no_delta(rel_path: &str, kind: HashKind) { let _guard = set_hash_kind(kind); diff --git a/src/internal/pack/encode.rs b/src/internal/pack/encode.rs index b5c319cb..eb6713e9 100644 --- a/src/internal/pack/encode.rs +++ b/src/internal/pack/encode.rs @@ -759,8 +759,13 @@ mod tests { } else { 2u64 * 1024 * 1024 * 1024 }; - let max_pack_size = usize::try_from(max_pack_size_u64) - .expect("pack size cap should fit in usize for this target"); + let max_pack_size = usize::try_from(max_pack_size_u64).unwrap_or_else(|_| { + panic!( + "internal assertion failed: pack size cap {} does not fit in usize on this \ + target; this should be unreachable given the target_pointer_width configuration", + max_pack_size_u64 + ) + }); let mut p = Pack::new( None, Some(max_pack_size), // 6GB on 64-bit, 2GB on 32-bit