{"record":{"id":"078211a88e05a787","repo":"MuntashirAkon/AppManager","slug":"block-overrun-while-expanding-rle-in-mtf-lastshadow-exceeds","errorCode":null,"errorMessage":"Block overrun while expanding RLE in MTF, \" + lastShadow + \" exceeds \" + limitLast","messagePattern":"Block overrun while expanding RLE in MTF, \" \\+ lastShadow \\+ \" exceeds \" \\+ limitLast","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java","lineNumber":654,"sourceCode":"                    }\n                    final int tmp = zvec - base_zt[zn];\n                    checkBounds(tmp, MAX_ALPHA_SIZE, \"zvec\");\n                    nextSym = perm_zt[tmp];\n                }\n                checkBounds(s, this.data.ll8.length, \"s\");\n\n                final int yy0 = yy[0];\n                checkBounds(yy0, 256, \"yy\");\n                final byte ch = seqToUnseq[yy0];\n                unzftab[ch & 0xff] += s + 1;\n\n                final int from = ++lastShadow;\n                lastShadow += s;\n                checkBounds(lastShadow, this.data.ll8.length, \"lastShadow\");\n                Arrays.fill(ll8, from, lastShadow + 1, ch);\n\n                if (lastShadow >= limitLast) {\n                    throw new IOException(\"Block overrun while expanding RLE in MTF, \"\n                            + lastShadow + \" exceeds \" + limitLast);\n                }\n            } else {\n                if (++lastShadow >= limitLast) {\n                    throw new IOException(\"Block overrun in MTF, \"\n                            + lastShadow + \" exceeds \" + limitLast);\n                }\n                checkBounds(nextSym, 256 + 1, \"nextSym\");\n\n                final char tmp = yy[nextSym - 1];\n                checkBounds(tmp, 256, \"yy\");\n                unzftab[seqToUnseq[tmp] & 0xff]++;\n                ll8[lastShadow] = seqToUnseq[tmp];\n\n                /*\n                 * This loop is hammered during decompression, hence avoid\n                 * native method call overhead of System.arraycopy for very\n                 * small ranges to copy.","sourceCodeStart":636,"sourceCodeEnd":672,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java#L636-L672","documentation":"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.","triggerScenarios":"BZip2CompressorInputStream.read() during block decoding: an RLE run symbol pushes lastShadow beyond limitLast after checkBounds(lastShadow, ll8.length, ...) has passed.","commonSituations":"Corrupted or truncated archives, crafted inputs claiming shorter blocks than they decode to, wrong bytes fed to the decompressor.","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."],"exampleFix":"// before\nIOUtils.copy(bz, out); // unbounded, throws raw IOException on corrupt block\n// after\ntry {\n    IOUtils.copy(bz, LimitedOutputStream.bounded(out, maxBytes));\n} catch (IOException e) {\n    throw new CorruptArchiveException(\"bzip2 block overrun: \" + e.getMessage(), e);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    IOUtils.copy(bz, boundedOut);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Block overrun\"))\n        throw new CorruptArchiveException(\"bzip2 block overrun: \" + e.getMessage(), e);\n    throw e;\n}","preventionTips":["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"],"tags":["io","bzip2","corrupt-input","rle","buffer-overflow-guard"],"backgroundTag":"corrupt-input-data","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}