MuntashirAkon/AppManager · error · IOException

Block overrun in MTF, " + lastShadow + " exceeds " + limitLa

Error message

Block overrun in MTF, " + lastShadow + " exceeds " + limitLast

What it means

In the MTF (move-to-front) decoding stage, a plain symbol increments lastShadow; if it reaches or exceeds limitLast the block produced more output than its declared size, indicating corruption. This guard prevents writing past the block's bounds.

Source

Thrown at app/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java:659

                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.
                 */
                if (nextSym <= 16) {
                    for (int j = nextSym - 1; j > 0;) {
                        yy[j] = yy[--j];
                    }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Re-verify/re-download the archive; the data is corrupt.
  2. Don't reuse or resume partially read streams; decompress from the beginning.
  3. Catch IOException and handle as 'corrupt input', not as a library bug.
  4. Constrain decompressed output size for untrusted inputs (decompression-bomb defense).

Example fix

// before
readAll(bz); // crashes with raw IOException
// after
try { readAll(bz); }
catch (IOException e) { log.warn("Corrupt bzip2 member skipped: " + e.getMessage()); }
Defensive patterns

Strategy: try-catch

Try / catch

try { readAll(bz); }
catch (IOException e) {
    log.warn("Skipping corrupt bzip2 member: {}", e.getMessage());
}

Prevention

When it happens

Trigger: BZip2CompressorInputStream.read() while decoding a bzip2 block: an MTF symbol increments lastShadow to >= limitLast.

Common situations: Bit-corrupted .bz2 files, truncated transfers, hostile/fuzzed inputs, wrong decoder fed non-bzip2 bytes.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/29506fc882ceb355. Report an issue: GitHub.