Skip to content

Commit 0dd5a9d

Browse files
authored
inout: fix output buffer length check in InOutBufReserved::into_padded_blocks (#1560)
`InOutBufReserved` only guarantees that the output buffer is at least as large as the input buffer. During the refactoring in #1556, the check that ensured the output buffer had enough space for the tail block was accidentally removed. Lack of the check allows to break the invariant important for the unsafe code in `PaddedInOutBuf` methods.
1 parent 7fc1967 commit 0dd5a9d

3 files changed

Lines changed: 74 additions & 4 deletions

File tree

‎inout/CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## 0.2.4 (UNRELEASED)
8+
### Fixed
9+
- Output buffer length check in `InOutBufReserved::into_padded_blocks` ([#1559])
10+
11+
[#1559]: https://github.com/RustCrypto/utils/pull/1559
12+
713
## 0.2.3 (2026-10-14)
814
### Fixed
915
- List of safety pre-conditions in `InOutBufReserved::from_raw` docs ([#1502])

‎inout/src/reserved.rs‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,30 @@ impl<'inp, 'out> InOutBufReserved<'inp, 'out, u8> {
175175
BS: ArraySize,
176176
{
177177
use block_padding::PaddedData;
178+
let out_blocks_len = self.out_len / BS::USIZE;
179+
178180
let (blocks, tail_block) = match P::pad_detached(self.get_in()) {
179-
PaddedData::Pad { blocks, tail_block } => (blocks, Some(tail_block)),
180-
PaddedData::NoPad { blocks } => (blocks, None),
181+
PaddedData::Pad { blocks, tail_block } => {
182+
// If tail block is present, the output buffer must contain
183+
// at least `blocks.len() + 1` full blocks
184+
if out_blocks_len <= blocks.len() {
185+
return Err(PadError);
186+
}
187+
(blocks, Some(tail_block))
188+
}
189+
PaddedData::NoPad { blocks } => {
190+
// If tail block is not present, the output buffer must contain
191+
// at least `blocks.len()` full blocks
192+
if out_blocks_len < blocks.len() {
193+
return Err(PadError);
194+
}
195+
(blocks, None)
196+
}
181197
PaddedData::Error => return Err(PadError),
182198
};
183199

184-
assert_eq!(blocks.len(), self.in_len / BS::USIZE);
200+
// This invariant must hold for all valid padding implementations.
201+
debug_assert_eq!(blocks.len(), self.in_len / BS::USIZE);
185202

186203
Ok(PaddedInOutBuf {
187204
in_ptr: blocks.as_ptr(),

‎inout/tests/reserved.rs‎

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use hybrid_array::sizes::U16;
44
use inout::{
55
InOutBufReserved,
6-
block_padding::{Iso7816, Padding},
6+
block_padding::{Iso7816, Padding, Pkcs7},
77
};
88

99
#[test]
@@ -29,3 +29,50 @@ fn padded_inout_buf_reserved() {
2929
let out = padded_blocks.into_out();
3030
assert_eq!(out, &[0xFF; 48]);
3131
}
32+
33+
#[test]
34+
fn reserved_invalid_lengths() {
35+
let msg = [42; 32];
36+
let mut buf = [0; 32];
37+
38+
for msg_len in 0..msg.len() {
39+
let msg = &msg[..msg_len];
40+
for out_len in 0..buf.len() {
41+
let out_buf = &mut buf[..out_len];
42+
43+
let reserved = match InOutBufReserved::from_slices(msg, out_buf) {
44+
Ok(r) => r,
45+
Err(_) => {
46+
assert!(msg_len > out_len);
47+
continue;
48+
}
49+
};
50+
51+
assert!(msg_len <= out_len);
52+
53+
let full_blocks = msg_len / 16;
54+
let out_blocks = out_len / 16;
55+
56+
let mut padded = match reserved.into_padded_blocks::<Pkcs7, U16>() {
57+
Ok(r) => r,
58+
Err(_) => {
59+
assert!(out_blocks <= full_blocks);
60+
continue;
61+
}
62+
};
63+
64+
assert!(out_blocks > full_blocks);
65+
66+
// Access the referenced memory to check it with Miri
67+
let mut full_blocks = padded.get_blocks();
68+
full_blocks.get_in().to_vec();
69+
full_blocks.get_out().to_vec();
70+
71+
let mut tail_block = padded.get_tail_block().unwrap();
72+
tail_block.get_in().to_vec();
73+
tail_block.get_out().to_vec();
74+
75+
padded.into_out().to_vec();
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)