Check position value when decoding cursor
What changed, and why it matters
This commit fixes a small but real integer-conversion bug when reading a witness data cursor on 16-bit platforms. Previously a 32-bit position value was silently truncated to 16 bits, which could make the code read the wrong memory offset or return an incorrect index. The fix validates the conversion and returns None if the value does not fit in a usize. On normal 32-bit and 64-bit systems the bug is not reachable because usize is already large enough.
Treat as a minor hardening patch. No urgent action needed for typical 32-bit/64-bit deployments, but include in the next release for correctness and 16-bit target safety. Review whether any other u32/usize casts in the crate lack try_from guards.
Security signals we found
Integer truncation / wrap on cast from u32 to usize
Potential incorrect memory offset / index on 16-bit platforms
Missing bounds check replaced with try_from validation
Low-level serialization primitive in Bitcoin witness handling
Evidence from the diff
decode_cursor in primitives/src/witness.rs previously did u32::from_ne_bytes(…) as usize. On 16-bit targets usize is 16 bits, so the cast truncates the 32-bit cursor value. The patch uses usize::try_from(pos).ok() so an out-of-range cursor causes the function to return None instead of a wrapped value. This is a defensive correctness fix; the affected function is part of the Witness serialization/deserialization path in rust-bitcoin primitives.
Changed components
primitives/src/witness.rsWitness cursor decodingdecode_cursor helperInspect captured patch +2 / −1
diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs
index a4d3607a..ac830497 100644
--- a/primitives/src/witness.rs
+++ b/primitives/src/witness.rs
@@ -269,7 +269,8 @@ fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value:
#[inline]
fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<usize> {
let start = start_of_indices + index * 4;
- bytes.get_array::<4>(start).map(|index_bytes| u32::from_ne_bytes(*index_bytes) as usize)
+ let pos = bytes.get_array::<4>(start).map(|index_bytes| u32::from_ne_bytes(*index_bytes))?;
+ usize::try_from(pos).ok()
}
/// The encoder for the [`Witness`] type.
Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.