Skip to content

NullPointerException in PdfReader.readDecryptedDocObj() when /Encrypt trailer entry references a missing xref object #1588

Description

@colinagel

PdfReader throws an uncaught NullPointerException (instead of a proper InvalidPdfException,
or simply treating the document as unencrypted) when the trailer's /Encrypt entry is an
indirect reference to an object number that does not exist / has no entry in the xref table.

Root cause, in readDecryptedDocObj():

PdfObject encDic = trailer.get(PdfName.ENCRYPT);
if (encDic == null || encDic.toString().equals("null")) {
    return;
}
encryptionError = true;
byte[] encryptionKey = null;
encrypted = true;
PdfDictionary enc = (PdfDictionary) getPdfObject(encDic);   // PdfReader.java:1424 — can return null
...
PdfObject filter = getPdfObjectRelease(enc.get(PdfName.FILTER)); // PdfReader.java:1449 — NPE

getPdfObject(encDic) resolves the indirect reference through the xref table and returns null
when the referenced object number has no xref entry (malformed/non-conformant producer). enc is
never null-checked before being dereferenced on the next line.

Other tools (poppler's pdfinfo, qpdf) open the same kind of malformed file without error and
correctly report it as not encrypted — the dangling /Encrypt reference is simply ignored.
OpenPDF is the only reader in our pipeline that crashes on this class of file.

To Reproduce

  1. Sample Code — minimal malformed PDF (/Size 5, but object 4 referenced by /Encrypt has no
    real xref entry):
%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] >>
endobj
xref
0 5
0000000000 65535 f 
0000000009 00000 n 
0000000058 00000 n 
0000000115 00000 n 
0000000000 00000 f 
trailer
<< /Size 5 /Root 1 0 R /Encrypt 4 0 R >>
startxref
186
%%EOF
  1. Unit-Test:
import org.openpdf.text.pdf.PdfReader;
import org.openpdf.text.pdf.RandomAccessFileOrArray;

public class TestReproPartial {
    public static void main(String[] args) throws Exception {
        var raf = new RandomAccessFileOrArray("malformed_encrypt.pdf");
        PdfReader r = new PdfReader(raf, new byte[0]); // partial-read path
        System.out.println("Opened OK, encrypted=" + r.isEncrypted());
    }
}

Output (verified against openpdf-3.0.3.jar):

java.lang.NullPointerException: Cannot invoke "org.openpdf.text.pdf.PdfDictionary.get(org.openpdf.text.pdf.PdfName)" because "enc" is null
    at org.openpdf.text.pdf.PdfReader.readDecryptedDocObj(PdfReader.java:1449)
    at org.openpdf.text.pdf.PdfReader.readDocObjPartial(PdfReader.java:1796)
    at org.openpdf.text.pdf.PdfReader.readPdfPartial(PdfReader.java:1387)
    at org.openpdf.text.pdf.PdfReader.<init>(PdfReader.java:285)

Note: the simpler new PdfReader("malformed_encrypt.pdf") constructor (non-partial path) also
fails, but wraps the same NullPointerException into an InvalidPdfException at
PdfReader.readPdf(PdfReader.java:1348) — so the bug is reachable either way, just surfaced
differently depending on which constructor is used.

Expected behavior

PdfReader should not throw a bare NullPointerException. It should either:

  • treat the document as unencrypted when the /Encrypt dictionary can't be resolved (matching
    poppler/qpdf behavior), or
  • throw a descriptive InvalidPdfException explaining that the encryption dictionary reference
    is broken.

System

  • OS: Linux (server-side; issue is not OS-specific, reproduced on the reader/parser)
  • OpenPDF version: 3.0.3

Additional context

Found while processing real-world PDFs generated by a non-standard/home-grown producer
(Creator: PDFGen versão 0.1 beta), consistent with a hand-rolled trailer containing a stray
/Encrypt N 0 R reference to an object number beyond what's actually defined. The minimal PDF
above reproduces the exact same code path and line numbers as the original crash, without
exposing any real document content.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions