mirror of https://github.com/apache/poi.git
#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:
parent
cce7a41807
commit
39dbbe9c41
|
@ -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
|
||||||
|
|
|
@ -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.
Loading…
Reference in New Issue