Merge branch 'bugfix/PDFBOX-4014_Malformed_pathological_malicious_input_can_lead_to_infinite_looping'
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
index a3a6394..4170ff1 100644
--- a/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/SymbolDictionary.java
@@ -73,7 +73,7 @@
private int amountOfExportSymbolss;
/** Number of new symbols, 7.4.2.1.5 */
- private int amountOfNewSymbolss;
+ private int amountOfNewSymbols;
/** Further parameters */
private SegmentHeader segmentHeader;
@@ -114,7 +114,7 @@
public SymbolDictionary() {
}
- public SymbolDictionary(SubInputStream subInputStream, SegmentHeader segmentHeader) throws IOException {
+ public SymbolDictionary(final SubInputStream subInputStream, final SegmentHeader segmentHeader) throws IOException {
this.subInputStream = subInputStream;
this.segmentHeader = segmentHeader;
}
@@ -231,7 +231,7 @@
}
private void readAmountOfNewSymbols() throws IOException {
- amountOfNewSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
+ amountOfNewSymbols = (int) subInputStream.readBits(32); // & 0xffffffff;
}
private void setInSyms() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
@@ -328,12 +328,12 @@
}
/* 6.5.5 1) */
- newSymbols = new Bitmap[amountOfNewSymbolss];
+ newSymbols = new Bitmap[amountOfNewSymbols];
/* 6.5.5 2) */
int[] newSymbolsWidths = null;
if (isHuffmanEncoded && !useRefinementAggregation) {
- newSymbolsWidths = new int[amountOfNewSymbolss];
+ newSymbolsWidths = new int[amountOfNewSymbols];
}
setSymbolsArray();
@@ -343,7 +343,7 @@
amountOfDecodedSymbols = 0;
/* 6.5.5 4 a) */
- while (amountOfDecodedSymbols != amountOfNewSymbolss) {
+ while (amountOfDecodedSymbols < amountOfNewSymbols) {
/* 6.5.5 4 b) */
heightClassHeight += decodeHeightClassDeltaHeight();
@@ -358,9 +358,15 @@
/* 4 c) i) */
final long differenceWidth = decodeDifferenceWidth();
- // If result is OOB, then all the symbols in this height
- // class has been decoded; proceed to step 4 d)
- if (differenceWidth == Long.MAX_VALUE) {
+ /*
+ * If result is OOB, then all the symbols in this height
+ * class has been decoded; proceed to step 4 d). Also exit, if the expected number of
+ * symbols have been decoded.
+ *
+ * The latter exit condition guards against pathological cases where a symbol's
+ * DW never contains OOB and thus never terminates.
+ */
+ if (differenceWidth == Long.MAX_VALUE || amountOfDecodedSymbols >= amountOfNewSymbols) {
break;
}
@@ -711,10 +717,10 @@
}
}
- private void setExportedSymbols(int[] toExportFlags) {
+ private void setExportedSymbols(final int[] toExportFlags) {
exportSymbols = new ArrayList<Bitmap>(amountOfExportSymbolss);
- for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbolss; i++) {
+ for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbols; i++) {
if (toExportFlags[i] == 1) {
if (i < amountOfImportedSymbolss) {
@@ -729,9 +735,9 @@
private int[] getToExportFlags() throws IOException, InvalidHeaderValueException {
int currentExportFlag = 0;
long exRunLength = 0;
- final int[] exportFlags = new int[amountOfImportedSymbolss + amountOfNewSymbolss];
+ final int[] exportFlags = new int[amountOfImportedSymbolss + amountOfNewSymbols];
- for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss + amountOfNewSymbolss; exportIndex += exRunLength) {
+ for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss + amountOfNewSymbols; exportIndex += exRunLength) {
if (isHuffmanEncoded) {
exRunLength = StandardTables.getTable(1).decode(subInputStream);
@@ -776,9 +782,9 @@
*/
private int getSbSymCodeLen() throws IOException {
if (isHuffmanEncoded) {
- return Math.max((int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2))), 1);
+ return Math.max((int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbols) / Math.log(2))), 1);
} else {
- return (int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / Math.log(2)));
+ return (int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbols) / Math.log(2)));
}
}
diff --git a/src/main/java/org/apache/pdfbox/jbig2/segments/TextRegion.java b/src/main/java/org/apache/pdfbox/jbig2/segments/TextRegion.java
index 797729c..200b036 100644
--- a/src/main/java/org/apache/pdfbox/jbig2/segments/TextRegion.java
+++ b/src/main/java/org/apache/pdfbox/jbig2/segments/TextRegion.java
@@ -32,8 +32,8 @@
import org.apache.pdfbox.jbig2.decoder.huffman.EncodedTable;
import org.apache.pdfbox.jbig2.decoder.huffman.FixedSizeTable;
import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable;
-import org.apache.pdfbox.jbig2.decoder.huffman.StandardTables;
import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable.Code;
+import org.apache.pdfbox.jbig2.decoder.huffman.StandardTables;
import org.apache.pdfbox.jbig2.err.IntegerMaxValueException;
import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
import org.apache.pdfbox.jbig2.image.Bitmaps;
@@ -239,6 +239,14 @@
private void readAmountOfSymbolInstances() throws IOException {
amountOfSymbolInstances = subInputStream.readBits(32) & 0xffffffff;
+
+ // sanity check: don't decode more than one symbol per pixel
+ long pixels = (long) regionInfo.getBitmapWidth() * (long) regionInfo.getBitmapHeight();
+ if (pixels < amountOfSymbolInstances) {
+ log.warn("Limiting number of decoded symbol instances to one per pixel (" + pixels + " instead of "
+ + amountOfSymbolInstances + ")");
+ amountOfSymbolInstances = pixels;
+ }
}
private void getSymbols() throws IOException, IntegerMaxValueException, InvalidHeaderValueException {
@@ -394,7 +402,7 @@
/* Last two sentences of 6.4.5 2) */
long firstS = 0;
- int instanceCounter = 0;
+ long instanceCounter = 0;
/* 6.4.5 3 a) */
while (instanceCounter < amountOfSymbolInstances) {
@@ -420,11 +428,16 @@
} else {
/* 6.4.8 */
final long idS = decodeIdS();
- /*
- * If result is OOB, then all the symbol instances in this strip have been decoded;
- * proceed to step 3 d) respectively 3 b)
+
+ /*
+ * If result is OOB, then all the symbol instances in this strip have been decoded; proceed to step
+ * 3 d) respectively 3 b). Also exit, if the expected number of instances have been decoded.
+ *
+ * The latter exit condition guards against pathological cases where a strip's S never contains OOB
+ * and thus never terminates as illustrated in
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=450971 case pdfium-loop2.pdf.
*/
- if (idS == Long.MAX_VALUE)
+ if (idS == Long.MAX_VALUE || instanceCounter >= amountOfSymbolInstances)
break;
currentS += (idS + sbdsOffset);