Skip to content

Commit 8f28ec2

Browse files
committed
Fix bug in decoding residuals
A partition order could occur, such that the block size was not a multiple of 2^order. Computation of the number of samples per partition did not account for this case, rounding down due to the bit shift. This meant that we would not fill the entire decode buffer. Claxon does not zero the decode buffer because it is (should be) overwritten anyway, and in the case of a format error, where the buffer might be only partially full, the buffer is not exposed again. Furthermore, the way decoding works in most places, is that we fill the entire buffer, just by looping to fill it. If the input bitstream does not contain enough data to fill the buffer, then that's a format error. In a few places though, we need to slice up the buffer before decoding into it: for decoding individual channels, and also for decoding residuals, which are split into partitions. This particular format error was especially nasty because it did not cause a format error down the line. Instead, it caused the buffer to be sliced in a way where the slices together did not cover the entire buffer, and so parts of uninitialized memory could remain in the buffer. Thanks a lot to Sergey "Shnatsel" Davidoff for reporting this bug, together with elaborate steps to reproduce that allowed me to pinpoint the cause quickly.
1 parent cd82be3 commit 8f28ec2

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

src/subframe.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -254,35 +254,48 @@ fn decode_residual<R: ReadBytes>(input: &mut Bitstream<R>,
254254
// most 2^16 - 1 samples in the block. No values have been marked as
255255
// invalid by the specification though.
256256
let n_partitions = 1u32 << order;
257-
let n_samples = block_size >> order;
257+
let n_samples_per_partition = block_size >> order;
258+
259+
// The partitions together must fill the block. If the block size is not a
260+
// multiple of 2^order; if we shifted off some bits, then we would not fill
261+
// the entire block. Such a partition order is invalid for this block size.
262+
if block_size & (n_partitions - 1) as u16 != 0 {
263+
return fmt_err("invalid partition order")
264+
}
265+
266+
// NOTE: the check above checks that block_size is a multiple of n_partitions
267+
// (this works because n_partitions is a power of 2). The check below is
268+
// equivalent but more expensive.
269+
debug_assert_eq!(n_partitions * n_samples_per_partition as u32, block_size as u32);
270+
258271
let n_warm_up = block_size - buffer.len() as u16;
259272

260273
// The partition size must be at least as big as the number of warm-up
261274
// samples, otherwise the size of the first partition is negative.
262-
if n_warm_up > n_samples {
275+
if n_warm_up > n_samples_per_partition {
263276
return fmt_err("invalid residual");
264277
}
265278

266279
// Finally decode the partitions themselves.
267280
match partition_type {
268281
RicePartitionType::Rice => {
269282
let mut start = 0;
270-
let mut len = n_samples - n_warm_up;
283+
let mut len = n_samples_per_partition - n_warm_up;
271284
for _ in 0..n_partitions {
272285
let slice = &mut buffer[start..start + len as usize];
273286
try!(decode_rice_partition(input, slice));
274287
start = start + len as usize;
275-
len = n_samples;
288+
len = n_samples_per_partition;
276289
}
277290
}
278291
RicePartitionType::Rice2 => {
279292
let mut start = 0;
280-
let mut len = n_samples - n_warm_up;
293+
let mut len = n_samples_per_partition - n_warm_up;
281294
for _ in 0..n_partitions {
282295
let slice = &mut buffer[start..start + len as usize];
283296
try!(decode_rice2_partition(input, slice));
284297
start = start + len as usize;
285-
len = n_samples;
298+
len = n_samples_per_partition;
286299
}
287300
}
288301
}

0 commit comments

Comments
 (0)