1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
Description: Fix an infinite loop on corrupted PPT file (CVE-2014-9527).
This patch can be dropped after upgrading to Apache POI 3.11 or later.
Origin: backport, http://svn.apache.org/r1643680
Bug: https://issues.apache.org/bugzilla/show_bug.cgi?id=57272
diff --git a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
index 420bd38..e4128c9 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/HSLFSlideShow.java
@@ -20,16 +20,16 @@ package org.apache.poi.hslf;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
import org.apache.poi.POIDocument;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
@@ -269,41 +269,66 @@ public final class HSLFSlideShow extends POIDocument {
_records = read(_docstream, (int)currentUser.getCurrentEditOffset());
}
- private Record[] read(byte[] docstream, int usrOffset){
- ArrayList<Integer> lst = new ArrayList<Integer>();
- HashMap<Integer,Integer> offset2id = new HashMap<Integer,Integer>();
+ private Record[] read(byte[] docstream, int usrOffset){
+ //sort found records by offset.
+ //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
+ NavigableMap<Integer,Record> records = new TreeMap<Integer,Record>(); // offset -> record
+ Map<Integer,Integer> persistIds = new HashMap<Integer,Integer>(); // offset -> persistId
+ initRecordOffsets(docstream, usrOffset, records, persistIds);
+
+ for (Map.Entry<Integer,Record> entry : records.entrySet()) {
+ Integer offset = entry.getKey();
+ Record record = entry.getValue();
+ Integer persistId = persistIds.get(offset);
+ if (record == null) {
+ // all plain records have been already added,
+ // only new records need to be decrypted (tbd #35897)
+ record = Record.buildRecordAtOffset(docstream, offset);
+ entry.setValue(record);
+ }
+
+ if (record instanceof PersistRecord) {
+ ((PersistRecord)record).setPersistId(persistId);
+ }
+ }
+
+ return records.values().toArray(new Record[records.size()]);
+ }
+
+ private void initRecordOffsets(byte[] docstream, int usrOffset, NavigableMap<Integer,Record> recordMap, Map<Integer,Integer> offset2id) {
while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
- lst.add(usrOffset);
+ recordMap.put(usrOffset, usr);
int psrOffset = usr.getPersistPointersOffset();
-
PersistPtrHolder ptr = (PersistPtrHolder)Record.buildRecordAtOffset(docstream, psrOffset);
- lst.add(psrOffset);
- Hashtable<Integer,Integer> entries = ptr.getSlideLocationsLookup();
- for(Integer id : entries.keySet()) {
- Integer offset = entries.get(id);
- lst.add(offset);
+ recordMap.put(psrOffset, ptr);
+
+ for(Map.Entry<Integer,Integer> entry : ptr.getSlideLocationsLookup().entrySet()) {
+ Integer offset = entry.getValue();
+ Integer id = entry.getKey();
+ recordMap.put(offset, null); // reserve a slot for the record
offset2id.put(offset, id);
}
usrOffset = usr.getLastUserEditAtomOffset();
- }
- //sort found records by offset.
- //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
- Integer a[] = lst.toArray(new Integer[lst.size()]);
- Arrays.sort(a);
- Record[] rec = new Record[lst.size()];
- for (int i = 0; i < a.length; i++) {
- Integer offset = a[i];
- rec[i] = Record.buildRecordAtOffset(docstream, offset.intValue());
- if(rec[i] instanceof PersistRecord) {
- PersistRecord psr = (PersistRecord)rec[i];
- Integer id = offset2id.get(offset);
- psr.setPersistId(id.intValue());
+
+ // check for corrupted user edit atom and try to repair it
+ // if the next user edit atom offset is already known, we would go into an endless loop
+ if (usrOffset > 0 && recordMap.containsKey(usrOffset)) {
+ // a user edit atom is usually located 36 byte before the smallest known record offset
+ usrOffset = recordMap.firstKey()-36;
+ // check that we really are located on a user edit atom
+ int ver_inst = LittleEndian.getUShort(docstream, usrOffset);
+ int type = LittleEndian.getUShort(docstream, usrOffset+2);
+ int len = LittleEndian.getInt(docstream, usrOffset+4);
+ if (ver_inst == 0 && type == 4085 && (len == 0x1C || len == 0x20)) {
+ logger.log(POILogger.WARN, "Repairing invalid user edit atom");
+ usr.setLastUserEditAtomOffset(usrOffset);
+ } else {
+ throw new CorruptPowerPointFileException("Powerpoint document contains invalid user edit atom");
+ }
}
}
-
- return rec;
}
/**
@@ -332,18 +357,14 @@ public final class HSLFSlideShow extends POIDocument {
private void readPictures() throws IOException {
_pictures = new ArrayList<PictureData>();
- byte[] pictstream;
+ // if the presentation doesn't contain pictures - will use a null set instead
+ if (!directory.hasEntry("Pictures")) return;
- try {
- DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
- pictstream = new byte[entry.getSize()];
- DocumentInputStream is = directory.createDocumentInputStream("Pictures");
- is.read(pictstream);
- } catch (FileNotFoundException e){
- // Silently catch exceptions if the presentation doesn't
- // contain pictures - will use a null set instead
- return;
- }
+ DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
+ byte[] pictstream = new byte[entry.getSize()];
+ DocumentInputStream is = directory.createDocumentInputStream(entry);
+ is.read(pictstream);
+ is.close();
int pos = 0;
// An empty picture record (length 0) will take up 8 bytes
|