Check fseDecodeInterleaved has room for the third symbol
It has no symbol count to stop on and runs until the bitstream is spent, so its own room check is the only thing keeping it inside the caller's buffer. Both exhausted branches write a third symbol in the same pass. This is the 53-byte reproducer with the frame stripped away, so a regression points at the function rather than at a block somewhere.
This commit is contained in:
parent
c4bc910c05
commit
d1159ca695
1 changed files with 55 additions and 0 deletions
55
test/fse_interleaved_test.dart
Normal file
55
test/fse_interleaved_test.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:pure_zstd/src/bit_reader.dart';
|
||||
import 'package:pure_zstd/src/exception.dart';
|
||||
import 'package:pure_zstd/src/fse.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
/// `fseDecodeInterleaved` has no symbol count to stop on: it runs until the
|
||||
/// bitstream is spent, so the only thing keeping it inside the caller's
|
||||
/// buffer is its own room check. Both of its exhausted branches write a third
|
||||
/// symbol in the same pass, so a check for two let `at` reach `outLimit`,
|
||||
/// which is one past the end.
|
||||
///
|
||||
/// `HuffmanTable` calls it with a 256-byte buffer, so overrunning it indexes
|
||||
/// `_weights[256]`. This is the same reproducer as the 53-byte frame in the
|
||||
/// corpus, with the frame stripped away: the weight table description and the
|
||||
/// bitstream the block carried, handed straight to the function.
|
||||
void main() {
|
||||
group('fseDecodeInterleaved', () {
|
||||
// The two bytes are the whole FSE table description: accuracy log 5 and a
|
||||
// distribution that gives the stream below two symbols per state pair.
|
||||
final description = Uint8List.fromList([0x10, 0x3F]);
|
||||
|
||||
// 33 bytes of 0xAA and then the end marker, which is the stream the
|
||||
// crafted block put after the description.
|
||||
final stream = Uint8List.fromList([...List.filled(33, 0xAA), 0x02]);
|
||||
|
||||
test('refuses a stream longer than the caller has room for', () {
|
||||
final table = FseTable(6);
|
||||
expect(readFseTable(table, description, 0, description.length, 255), 2);
|
||||
|
||||
final bits = ReverseBitReader()..reset(stream, 0, stream.length);
|
||||
final weights = Uint8List(256);
|
||||
|
||||
expect(
|
||||
() => fseDecodeInterleaved(table, bits, weights, 0, weights.length),
|
||||
throwsA(isA<ZstdException>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('fills the buffer exactly when there is room for the last pass', () {
|
||||
final table = FseTable(6);
|
||||
readFseTable(table, description, 0, description.length, 255);
|
||||
|
||||
// Same stream, a buffer with slack. The count it returns must be the
|
||||
// symbols it actually wrote, so the last pass cannot have been longer
|
||||
// than the room the check left.
|
||||
final bits = ReverseBitReader()..reset(stream, 0, stream.length);
|
||||
final room = Uint8List(1024);
|
||||
final written = fseDecodeInterleaved(table, bits, room, 0, room.length);
|
||||
expect(written, greaterThan(256));
|
||||
expect(written, lessThanOrEqualTo(room.length));
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue