From e4ee17c9e5e91e2f303d96e29b7086d212288e8e Mon Sep 17 00:00:00 2001 From: Maxim Date: Sat, 3 Oct 2026 00:49:45 +0300 Subject: [PATCH] Limit Type 4 function procedure nesting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../verapdf/pd/function/PDType4Function.java | 40 ++++++++++++++----- .../pd/function/PDType4FunctionTest.java | 22 ++++++++++ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/verapdf/pd/function/PDType4Function.java b/src/main/java/org/verapdf/pd/function/PDType4Function.java index dc418c69..fac0f931 100644 --- a/src/main/java/org/verapdf/pd/function/PDType4Function.java +++ b/src/main/java/org/verapdf/pd/function/PDType4Function.java @@ -38,9 +38,12 @@ public class PDType4Function extends PDFunction { private static final Logger LOGGER = Logger.getLogger(PDType4Function.class.getCanonicalName()); + + private static final int MAX_PROCEDURE_DEPTH = 64; private List operators; private List modifiedOperators; private FunctionParser parser; + private boolean operatorsWithProceduresFailed; protected PDType4Function(COSObject obj) { super(obj); @@ -83,9 +86,13 @@ public List parseOperators() { public void setOperators(List operators) { this.operators = operators; this.modifiedOperators = null; + this.operatorsWithProceduresFailed = false; } - private List getOperatorsWithProcedures() { + private List getOperatorsWithProcedures() throws PostScriptException { + if (operatorsWithProceduresFailed) { + throw new PostScriptException("Type 4 function operators could not be parsed"); + } if (modifiedOperators == null) { modifiedOperators = new ArrayList<>(); if (getOperators().isEmpty()) { @@ -95,22 +102,33 @@ private List getOperatorsWithProcedures() { if (PSOperatorsConstants.LEFT_CURLY_BRACE.equals(getOperators().get(0).getString())) { ops.next(); } - while (ops.hasNext()) { - COSObject obj = ops.next(); - if (obj != null) { - if (obj instanceof PSOperator && - PSOperatorsConstants.LEFT_CURLY_BRACE.equals(((PSOperator) obj).getOperator())) { - recursiveProcedure(ops, modifiedOperators); - } else { - modifiedOperators.add(obj); + List parsedOperators = new ArrayList<>(); + try { + while (ops.hasNext()) { + COSObject obj = ops.next(); + if (obj != null) { + if (obj instanceof PSOperator && + PSOperatorsConstants.LEFT_CURLY_BRACE.equals(((PSOperator) obj).getOperator())) { + recursiveProcedure(ops, parsedOperators, 0); + } else { + parsedOperators.add(obj); + } } } + } catch (PostScriptException e) { + operatorsWithProceduresFailed = true; + throw e; } + modifiedOperators = parsedOperators; } return modifiedOperators; } - private void recursiveProcedure(Iterator ops, List modifiedOperators) { + private void recursiveProcedure(Iterator ops, List modifiedOperators, int depth) + throws PostScriptException { + if (depth >= MAX_PROCEDURE_DEPTH) { + throw new PostScriptException("Type 4 function exceeded procedure recursion depth " + MAX_PROCEDURE_DEPTH); + } List proc = new ArrayList<>(); while (ops.hasNext()) { COSObject obj = ops.next(); @@ -118,7 +136,7 @@ private void recursiveProcedure(Iterator ops, List modifie break; } if (obj instanceof PSOperator && PSOperatorsConstants.LEFT_CURLY_BRACE.equals(((PSOperator) obj).getOperator())) { - recursiveProcedure(ops, proc); + recursiveProcedure(ops, proc, depth + 1); } proc.add(obj); } diff --git a/src/test/java/org/verapdf/pd/function/PDType4FunctionTest.java b/src/test/java/org/verapdf/pd/function/PDType4FunctionTest.java index 73c6960f..6650be8c 100644 --- a/src/test/java/org/verapdf/pd/function/PDType4FunctionTest.java +++ b/src/test/java/org/verapdf/pd/function/PDType4FunctionTest.java @@ -21,16 +21,23 @@ package org.verapdf.pd.function; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.converter.ConvertWith; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.verapdf.cos.COSName; import org.verapdf.cos.COSObject; +import org.verapdf.parser.postscript.PSOperator; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Stream; import static org.verapdf.pd.function.PDFunctionTestHelper.EPSILON; +import static org.verapdf.pd.function.PSOperatorsConstants.LEFT_CURLY_BRACE; +import static org.verapdf.pd.function.PSOperatorsConstants.RIGHT_CURLY_BRACE; public class PDType4FunctionTest { final PDType4Function func = new PDType4Function(new COSObject()); @@ -68,6 +75,21 @@ public void testGetResultWithInvalidOperators(@ConvertWith(PDFunctionTestHelper. Assertions.assertNull(func.getResult(operands)); } + @Test + public void testGetResultWithExcessiveProcedureNesting() { + List operators = new ArrayList<>(); + for (int i = 0; i < 8001; i++) { + operators.add(new PSOperator(COSName.construct(LEFT_CURLY_BRACE))); + } + for (int i = 0; i < 8001; i++) { + operators.add(new PSOperator(COSName.construct(RIGHT_CURLY_BRACE))); + } + + func.setOperators(operators); + Assertions.assertNull(func.getResult(Collections.emptyList())); + Assertions.assertNull(func.getResult(Collections.emptyList())); + } + private static Stream provideInvalidParameters() { return Stream.of( Arguments.of("{ add }", "2")