{"record":{"id":"04bdcd00324dacab","repo":"skylot/jadx","slug":"read-limit-exceeded","errorCode":null,"errorMessage":"Read limit exceeded","messagePattern":"Read limit exceeded","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"jadx-commons/jadx-zip/src/main/java/jadx/zip/io/LimitedInputStream.java","lineNumber":21,"sourceCode":"import java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class LimitedInputStream extends FilterInputStream {\n\tprivate final long maxSize;\n\n\tprivate long currentPos;\n\tprivate long markPos;\n\n\tpublic LimitedInputStream(InputStream in, long maxSize) {\n\t\tsuper(in);\n\t\tthis.maxSize = maxSize;\n\t}\n\n\tprivate void addAndCheckPos(long count) {\n\t\tcurrentPos += count;\n\t\tif (currentPos > maxSize) {\n\t\t\tthrow new IllegalStateException(\"Read limit exceeded\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic int read() throws IOException {\n\t\tint data = super.read();\n\t\tif (data != -1) {\n\t\t\taddAndCheckPos(1);\n\t\t}\n\t\treturn data;\n\t}\n\n\t@SuppressWarnings(\"NullableProblems\")\n\t@Override\n\tpublic int read(byte[] b, int off, int len) throws IOException {\n\t\tint count = super.read(b, off, len);\n\t\tif (count > 0) {\n\t\t\taddAndCheckPos(count);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-commons/jadx-zip/src/main/java/jadx/zip/io/LimitedInputStream.java#L3-L39","documentation":"A deliberate security guard. LimitedInputStream counts every byte read and throws IllegalStateException once currentPos exceeds maxSize, where maxSize is the entry's declared uncompressed size. It is an anti-zip-bomb measure: if the real decompressed content is larger than the header claims, reading stops immediately. Because it is an unchecked exception it propagates out of read()/read(byte[]) and up through getBytes()/getInputStream() consumers.","triggerScenarios":"Reading from an InputStream produced by FallbackZipParser when zipSecurity.useLimitedDataStream() is enabled, for an entry whose actual decompressed byte count exceeds entry.getUncompressedSize(). Any single-byte or bulk read past the limit trips addAndCheckPos().","commonSituations":"A crafted zip-bomb whose compressed data decompresses to more bytes than the (spoofed) uncompressed-size field declares; a legitimately corrupt entry with a wrong size header; an obfuscator/packer that writes inaccurate uncompressed sizes. Also reached during jadx CLI/GUI processing of untrusted APKs where the size limit is on by default.","solutions":["Treat this as evidence of a tampered or hostile archive; do not trust the entry's data.","Verify the archive against a known-good checksum and re-download if it mismatches.","If you control the source and the size header is simply wrong, repackage the zip with a correct uncompressed size.","Catch IllegalStateException at the call site and skip the entry rather than aborting the whole archive."],"exampleFix":"// before\nbyte[] bytes = zipEntry.getBytes();\n\n// after\nbyte[] bytes;\ntry {\n    bytes = zipEntry.getBytes();\n} catch (IllegalStateException limit) {\n    if (\"Read limit exceeded\".equals(limit.getMessage())) {\n        LOG.warn(\"Entry {} exceeds declared size (possible zip bomb), skipping\", zipEntry.getName());\n        continue;\n    }\n    throw limit;\n}","handlingStrategy":"validation","validationCode":"// The limit equals the entry's declared uncompressed size.\n// If you can read the header, sanity-check the declared size before reading:\nlong declared = entry.getUncompressedSize();\nif (declared < 0 || declared > MAX_ACCEPTABLE_ENTRY_SIZE) {\n    LOG.warn(\"Entry {} declares suspicious uncompressed size {}, skipping\", entry.getName(), declared);\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    bytes = entry.getBytes();\n} catch (IllegalStateException e) {\n    if (\"Read limit exceeded\".equals(e.getMessage())) {\n        LOG.warn(\"Possible zip bomb in entry {}, skipping\", entry.getName());\n        continue;\n    }\n    throw e;\n}","preventionTips":["Keep the limited-data-stream guard enabled for untrusted archives (it is the zip-bomb defense).","Reject archives whose entries declare implausibly large uncompressed sizes up front.","Treat a Read limit exceeded as a security signal, not a recoverable data error."],"tags":["zip","security","zip-bomb","validation"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}