MuntashirAkon/AppManager · error · IOException
Block overrun while expanding RLE in MTF, " + lastShadow + "
Error message
Block overrun while expanding RLE in MTF, " + lastShadow + " exceeds " + limitLast
What it means
While reversing the MTF/RLE stage of a bzip2 block, the decoder expands run-length entries into the ll8 buffer. If the expanded position (lastShadow) passes the block's declared limit (limitLast), the block's uncompressed length is inconsistent with its contents, i.e. corrupted input, and decoding stops.
Source
Thrown at app/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java:654
}
final int tmp = zvec - base_zt[zn];
checkBounds(tmp, MAX_ALPHA_SIZE, "zvec");
nextSym = perm_zt[tmp];
}
checkBounds(s, this.data.ll8.length, "s");
final int yy0 = yy[0];
checkBounds(yy0, 256, "yy");
final byte ch = seqToUnseq[yy0];
unzftab[ch & 0xff] += s + 1;
final int from = ++lastShadow;
lastShadow += s;
checkBounds(lastShadow, this.data.ll8.length, "lastShadow");
Arrays.fill(ll8, from, lastShadow + 1, ch);
if (lastShadow >= limitLast) {
throw new IOException("Block overrun while expanding RLE in MTF, "
+ lastShadow + " exceeds " + limitLast);
}
} else {
if (++lastShadow >= limitLast) {
throw new IOException("Block overrun in MTF, "
+ lastShadow + " exceeds " + limitLast);
}
checkBounds(nextSym, 256 + 1, "nextSym");
final char tmp = yy[nextSym - 1];
checkBounds(tmp, 256, "yy");
unzftab[seqToUnseq[tmp] & 0xff]++;
ll8[lastShadow] = seqToUnseq[tmp];
/*
* This loop is hammered during decompression, hence avoid
* native method call overhead of System.arraycopy for very
* small ranges to copy.View on GitHub (pinned to 0152f468fc)
Solutions
- Treat the archive as corrupt: verify with `bzip2 -t` and re-obtain the file.
- Ensure the whole stream (not a partial copy) is supplied to the decompressor.
- Catch IOException and report/skip the corrupt block instead of retrying the same bytes.
- If inputs are untrusted, run decompression with limits (size/time caps) since such inputs may be hostile.
Example fix
// before
IOUtils.copy(bz, out); // unbounded, throws raw IOException on corrupt block
// after
try {
IOUtils.copy(bz, LimitedOutputStream.bounded(out, maxBytes));
} catch (IOException e) {
throw new CorruptArchiveException("bzip2 block overrun: " + e.getMessage(), e);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
IOUtils.copy(bz, boundedOut);
} catch (IOException e) {
if (e.getMessage().contains("Block overrun"))
throw new CorruptArchiveException("bzip2 block overrun: " + e.getMessage(), e);
throw e;
} Prevention
- Cap output size with a bounded OutputStream for untrusted input
- Supply complete, unmodified .bz2 files to the decompressor
- Treat these messages as corrupt-data, not library defects
- Keep commons-compress current — bounds checks fix crash/CVE-class bugs
When it happens
Trigger: BZip2CompressorInputStream.read() during block decoding: an RLE run symbol pushes lastShadow beyond limitLast after checkBounds(lastShadow, ll8.length, ...) has passed.
Common situations: Corrupted or truncated archives, crafted inputs claiming shorter blocks than they decode to, wrong bytes fed to the decompressor.
Related errors
- Corrupted input, " + name + " value negative
- Corrupted input, " + name + " value too big
- Corrupted input, nSelectors value negative
- Block overrun in MTF, " + lastShadow + " exceeds " + limitLa
- Stream corrupted
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/078211a88e05a787.
Report an issue: GitHub.