Skip to content

Commit 71c46f0

Browse files
committed
Merge rust-bitcoin#5050: primitives: Improve casts in witness module
7695ad8 primitives: Add private cast_to_usize_if_valid function (Tobin C. Harding) 61e6e5c Check position value when decoding cursor (Tobin C. Harding) cb88804 Remove stale comment (Tobin C. Harding) Pull request description: Two patches because the reason we can improve the casts is slightly different. ACKs for top commit: apoelstra: ACK 7695ad8; successfully ran local tests jrakibi: ACK 7695ad8 Tree-SHA512: 965384cdb923f5be0eaf2ee7792cd589771105dcf6f46f6b342ad1cc092ff39763463e90abb0d8976ff785e07cd079ba46fa854dfb122b81f250f0544401da93
2 parents c5d5df6 + 7695ad8 commit 71c46f0

File tree

1 file changed

+25
-8
lines changed

1 file changed

+25
-8
lines changed

primitives/src/witness.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,7 @@ impl Witness {
230230

231231
let mut slice = &self.content[pos..]; // Start of element.
232232
let element_len = compact_size::decode_unchecked(&mut slice);
233-
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
234-
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
235-
let end = element_len as usize;
233+
let end = cast_to_usize_if_valid(element_len)?;
236234
Some(&slice[..end])
237235
}
238236

@@ -266,11 +264,11 @@ fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value:
266264
.copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("larger than u32")));
267265
}
268266

269-
// This is duplicated in `bitcoin::blockdata::witness`, if you change them do so over there also.
270267
#[inline]
271268
fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<usize> {
272269
let start = start_of_indices + index * 4;
273-
bytes.get_array::<4>(start).map(|index_bytes| u32::from_ne_bytes(*index_bytes) as usize)
270+
let pos = bytes.get_array::<4>(start).map(|index_bytes| u32::from_ne_bytes(*index_bytes))?;
271+
usize::try_from(pos).ok()
274272
}
275273

276274
/// The encoder for the [`Witness`] type.
@@ -562,9 +560,7 @@ impl<'a> Iterator for Iter<'a> {
562560
let index = decode_cursor(self.inner, self.indices_start, self.current_index)?;
563561
let mut slice = &self.inner[index..]; // Start of element.
564562
let element_len = compact_size::decode_unchecked(&mut slice);
565-
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
566-
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
567-
let end = element_len as usize;
563+
let end = cast_to_usize_if_valid(element_len)?;
568564
self.current_index += 1;
569565
Some(&slice[..end])
570566
}
@@ -809,6 +805,27 @@ impl<'a> Arbitrary<'a> for Witness {
809805
}
810806
}
811807

808+
/// Cast a decoded length prefix to a `usize`.
809+
///
810+
/// This function is basically just defensive. For all sane use cases the length prefix should be
811+
/// less than `MAX_VEC_SIZE` (on a 32-bit machine). If the value is bigger that `u16::MAX` and we
812+
/// are on a 16-bit machine you'll likely hit an error later anyway, better to just check it now.
813+
///
814+
/// # 16-bits
815+
///
816+
/// The compact size may be bigger than what can be represented in a `usize` on a 16-bit machine but
817+
/// this shouldn't happen if we created the witness because one would get an OOM error before that.
818+
fn cast_to_usize_if_valid(n: u64) -> Option<usize> {
819+
/// Maximum size, in bytes, of a vector we are allowed to decode.
820+
const MAX_VEC_SIZE: u64 = 4_000_000;
821+
822+
if n > MAX_VEC_SIZE {
823+
return None;
824+
}
825+
826+
usize::try_from(n).ok()
827+
}
828+
812829
#[cfg(test)]
813830
mod test {
814831
#[cfg(feature = "alloc")]

0 commit comments

Comments
 (0)