#57272 - deadlock on corrupted PPT file

+ some refactoring in HSLFSlideShow, which will be necessary for cryptoapi support 

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1643680 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-12-07 14:34:19 +00:00
parent 93dffcb366
commit 21d2bc3cff
3 changed files with 88 additions and 48 deletions

View File

@ -20,16 +20,16 @@ package org.apache.poi.hslf;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.apache.poi.POIDocument; import org.apache.poi.POIDocument;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
@ -81,7 +81,7 @@ public final class HSLFSlideShow extends POIDocument {
// Embedded objects stored in storage records in the document stream, lazily populated. // Embedded objects stored in storage records in the document stream, lazily populated.
private ObjectData[] _objects; private ObjectData[] _objects;
/** /**
* Returns the underlying POIFSFileSystem for the document * Returns the underlying POIFSFileSystem for the document
* that is open. * that is open.
@ -195,6 +195,9 @@ public final class HSLFSlideShow extends POIDocument {
// Look for any other streams // Look for any other streams
readOtherStreams(); readOtherStreams();
} }
/** /**
* Constructs a new, empty, Powerpoint document. * Constructs a new, empty, Powerpoint document.
*/ */
@ -269,41 +272,67 @@ public final class HSLFSlideShow extends POIDocument {
_records = read(_docstream, (int)currentUser.getCurrentEditOffset()); _records = read(_docstream, (int)currentUser.getCurrentEditOffset());
} }
private Record[] read(byte[] docstream, int usrOffset){ private Record[] read(byte[] docstream, int usrOffset){
ArrayList<Integer> lst = new ArrayList<Integer>();
HashMap<Integer,Integer> offset2id = new HashMap<Integer,Integer>();
while (usrOffset != 0){
UserEditAtom usr = (UserEditAtom) Record.buildRecordAtOffset(docstream, usrOffset);
lst.add(usrOffset);
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);
offset2id.put(offset, id);
}
usrOffset = usr.getLastUserEditAtomOffset();
}
//sort found records by offset. //sort found records by offset.
//(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted) //(it is not necessary but SlideShow.findMostRecentCoreRecords() expects them sorted)
Integer a[] = lst.toArray(new Integer[lst.size()]); NavigableMap<Integer,Record> records = new TreeMap<Integer,Record>(); // offset -> record
Arrays.sort(a); Map<Integer,Integer> persistIds = new HashMap<Integer,Integer>(); // offset -> persistId
Record[] rec = new Record[lst.size()]; initRecordOffsets(docstream, usrOffset, records, persistIds);
for (int i = 0; i < a.length; i++) {
Integer offset = a[i]; for (Map.Entry<Integer,Record> entry : records.entrySet()) {
rec[i] = Record.buildRecordAtOffset(docstream, offset.intValue()); Integer offset = entry.getKey();
if(rec[i] instanceof PersistRecord) { Record record = entry.getValue();
PersistRecord psr = (PersistRecord)rec[i]; Integer persistId = persistIds.get(offset);
Integer id = offset2id.get(offset); if (record == null) {
psr.setPersistId(id.intValue()); // 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()]);
}
return rec; 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);
recordMap.put(usrOffset, usr);
int psrOffset = usr.getPersistPointersOffset();
PersistPtrHolder ptr = (PersistPtrHolder)Record.buildRecordAtOffset(docstream, psrOffset);
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();
// 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");
}
}
}
} }
/** /**
@ -324,34 +353,30 @@ public final class HSLFSlideShow extends POIDocument {
private void readOtherStreams() { private void readOtherStreams() {
// Currently, there aren't any // Currently, there aren't any
} }
/** /**
* Find and read in pictures contained in this presentation. * Find and read in pictures contained in this presentation.
* This is lazily called as and when we want to touch pictures. * This is lazily called as and when we want to touch pictures.
*/ */
@SuppressWarnings("unused")
private void readPictures() throws IOException { private void readPictures() throws IOException {
_pictures = new ArrayList<PictureData>(); _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"); DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
pictstream = new byte[entry.getSize()]; byte[] pictstream = new byte[entry.getSize()];
DocumentInputStream is = directory.createDocumentInputStream("Pictures"); DocumentInputStream is = directory.createDocumentInputStream(entry);
is.read(pictstream); is.read(pictstream);
} catch (FileNotFoundException e){ is.close();
// Silently catch exceptions if the presentation doesn't
// contain pictures - will use a null set instead
return;
}
int pos = 0; int pos = 0;
// An empty picture record (length 0) will take up 8 bytes // An empty picture record (length 0) will take up 8 bytes
while (pos <= (pictstream.length-8)) { while (pos <= (pictstream.length-8)) {
int offset = pos; int offset = pos;
// Image signature // Image signature
@SuppressWarnings("unused")
int signature = LittleEndian.getUShort(pictstream, pos); int signature = LittleEndian.getUShort(pictstream, pos);
pos += LittleEndian.SHORT_SIZE; pos += LittleEndian.SHORT_SIZE;
// Image type + 0xF018 // Image type + 0xF018

View File

@ -564,4 +564,19 @@ public final class TestBugs {
inputStream.close(); inputStream.close();
} }
} }
@Test
public void bug57272() throws Exception {
InputStream inputStream = new FileInputStream(_slTests.getFile("57272_corrupted_usereditatom.ppt"));
try {
SlideShow slideShow = new SlideShow(inputStream);
assertEquals(6, slideShow.getSlides().length);
SlideShow slideBack = HSLFTestDataSamples.writeOutAndReadBack(slideShow);
assertNotNull(slideBack);
assertEquals(6, slideBack.getSlides().length);
} finally {
inputStream.close();
}
}
} }

Binary file not shown.