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
