From 917b138831badcfa586eaa9a4af194bef3a9714a Mon Sep 17 00:00:00 2001 From: Karen Hanson Date: Wed, 26 Jan 2022 15:17:47 -0500 Subject: [PATCH] Safely exit infinite loops on AProfile.outlinesOk and checkItemOutline Other parts of the code have recursion detection, but here incorrect or mishandled escape characters can cause infinite loops. This safely exits the loop on either AProfile.outlinesOK() or AProfile.checkItemOutline() and logs a message, similar to how things are handled in PDFModule.buildOutlineItemProperty - uses the same message. --- .../hul/ois/jhove/module/pdf/AProfile.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java index 472ff8984..fc7fcf35b 100644 --- a/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java +++ b/jhove-modules/pdf-hul/src/main/java/edu/harvard/hul/ois/jhove/module/pdf/AProfile.java @@ -33,6 +33,13 @@ public final class AProfile extends PdfProfile * PRIVATE CLASS FIELDS. ******************************************************************/ + /* + * Map of visited nodes when walking through an outline.- used to stop + * infinite loops. These may be caused by mishandled escape characters + * when loading objects. + */ + protected Set _visitedOutlineNodes; + /* TaggedProfile to which this profile is linked. */ private TaggedProfile _taggedProfile; private boolean _levelA; @@ -822,6 +829,7 @@ private boolean checkUncalIntent () we save the time to do this test. */ private boolean outlinesOK () { + _visitedOutlineNodes = new HashSet(); if (!_module.getActionsExist ()) { return true; } @@ -833,11 +841,16 @@ private boolean outlinesOK () PdfDictionary item = (PdfDictionary) _module.resolveIndirectObject (outlineDict.get ("First")); while (item != null) { + _visitedOutlineNodes.add(item.getObjNumber()); if (!checkOutlineItem (item)) { return false; } - item = (PdfDictionary) _module.resolveIndirectObject + PdfDictionary next = (PdfDictionary) _module.resolveIndirectObject (((PdfDictionary) item).get ("Next")); + if (_visitedOutlineNodes.contains(next.getObjNumber())) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_129); + } + item = next; } } catch (Exception e) { @@ -861,12 +874,17 @@ private boolean checkOutlineItem (PdfDictionary item) _module.resolveIndirectObject (item.get ("First")); PdfDictionary next; while (child != null) { + _visitedOutlineNodes.add(child.getObjNumber()); if (!checkOutlineItem (child)) { return false; } next = (PdfDictionary) _module.resolveIndirectObject (child.get ("Next")); - if (next.getObjNumber() != child.getObjNumber()) { + Integer nextObjNum = next.getObjNumber(); + if (nextObjNum != child.getObjNumber()) { + if (_visitedOutlineNodes.contains(nextObjNum)) { + throw new PdfInvalidException(MessageConstants.PDF_HUL_129); + } child = next; } else { child = null;