We had
output[output_pos - distance];
where output_pos and distance are size_t and distance can be greater
than output_pos (because it refers to a place in the previous block).
The underflow is defined, leading to a big number, and when
sizeof(size_t) == sizeof(*uint8_t) the subsequent overflow works as
expected. But if size_t is smaller than a pointer, bad things will
happen.
This was found by OSSFuzz with
'UBSAN_OPTIONS=print_stacktrace=1:silence_unsigned_overflow=1'.
Credit to OSSFuzz.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
* the end of the expected block. That's fine, so long
* as it doesn't extend past the total output size.
*/
+ size_t i;
size_t end = output_pos + length;
+ uint8_t *here = output + output_pos;
+ uint8_t *there = here - distance;
if (end > output_size ||
previous_size + output_pos < distance ||
- unlikely(end < output_pos)) {
+ unlikely(end < output_pos || there > here)) {
return LZXPRESS_ERROR;
}
-
- for (; output_pos < end; output_pos++) {
- output[output_pos] = \
- output[output_pos - distance];
+ for (i = 0; i < length; i++) {
+ here[i] = there[i];
}
+ output_pos += length;
distance = 0;
length = 0;
}