Author: j16sdiz
Date: 2009-03-03 05:00:59 +0000 (Tue, 03 Mar 2009)
New Revision: 25876

Modified:
   trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
Log:
PNGFilter should throw DataFormatException

It was throwing ArrayIndexOutOfBoundException and NegativeIndexException for 
invalid images.

Modified: trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java        
2009-03-03 03:12:22 UTC (rev 25875)
+++ trunk/freenet/src/freenet/clients/http/filter/PNGFilter.java        
2009-03-03 05:00:59 UTC (rev 25876)
@@ -8,6 +8,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,7 +33,7 @@
 
 /**
  * Content filter for PNG's. Only allows valid chunks (valid CRC, known chunk 
type).
- *
+ * 
  * It can strip the timestamp and "text(.)*" chunks if asked to
  * 
  * FIXME: validate chunk contents where possible.
@@ -42,31 +43,22 @@
        private final boolean deleteText;
        private final boolean deleteTimestamp;
        private final boolean checkCRCs;
-       static final byte[] pngHeader =
-               {(byte) 137, (byte) 80, (byte) 78, (byte) 71, (byte) 13, (byte) 
10, (byte) 26, (byte) 10};
+       static final byte[] pngHeader = { (byte) 137, (byte) 80, (byte) 78, 
(byte) 71, (byte) 13, (byte) 10, (byte) 26,
+               (byte) 10 };
        static final String[] HARMLESS_CHUNK_TYPES = {
-               // http://www.w3.org/TR/PNG/
-               "tRNS",
-               "cHRM",
-               "gAMA",
-               "iCCP", // FIXME Embedded ICC profile: could this conceivably 
cause a web lookup?
-               "sBIT", // FIXME rather obscure ??
-               "sRGB",
-               "bKGD",
-               "hIST",
-               "pHYs",
-               "sPLT",
-               // APNG chunks (Firefox 3 will support APNG)
-               // http://wiki.mozilla.org/APNG_Specification
-               "acTL",
-               "fcTL",
-               "fdAT"
-               // MNG isn't supported by Firefox and IE because of lack of 
market demand. Big surprise
-               // given nobody supports it! It is supported by Konqueror 
though. Complex standard,
-               // not worth it for the time being.
-               
-               // This might be a useful source of info too (e.g. on private 
chunks):
-               // 
http://fresh.t-systems-sfr.com/unix/privat/pngcheck-2.3.0.tar.gz:a/pngcheck-2.3.0/pngcheck.c
+       // http://www.w3.org/TR/PNG/
+               "tRNS", "cHRM", "gAMA", "iCCP", // FIXME Embedded ICC profile: 
could this conceivably cause a web lookup?
+               "sBIT", // FIXME rather obscure ??
+               "sRGB", "bKGD", "hIST", "pHYs", "sPLT",
+               // APNG chunks (Firefox 3 will support APNG)
+               // http://wiki.mozilla.org/APNG_Specification
+               "acTL", "fcTL", "fdAT"
+       // MNG isn't supported by Firefox and IE because of lack of market 
demand. Big surprise
+       // given nobody supports it! It is supported by Konqueror though. 
Complex standard,
+       // not worth it for the time being.
+
+       // This might be a useful source of info too (e.g. on private chunks):
+       // 
http://fresh.t-systems-sfr.com/unix/privat/pngcheck-2.3.0.tar.gz:a/pngcheck-2.3.0/pngcheck.c
        };
 
        PNGFilter(boolean deleteText, boolean deleteTimestamp, boolean 
checkCRCs) {
@@ -78,9 +70,9 @@
        public Bucket readFilter(Bucket data, BucketFactory bf, String charset, 
HashMap<String, String> otherParams,
                FilterCallback cb) throws DataFilterException, IOException {
                Bucket output = readFilter(data, bf, charset, otherParams, cb, 
deleteText, deleteTimestamp, checkCRCs, null);
-               if(output != null)
+               if (output != null)
                        return output;
-               if(Logger.shouldLog(Logger.MINOR, this))
+               if (Logger.shouldLog(Logger.MINOR, this))
                        Logger.minor(this, "Need to modify PNG...");
                Bucket filtered = bf.makeBucket(data.size());
                OutputStream os = new 
BufferedOutputStream(filtered.getOutputStream());
@@ -110,21 +102,21 @@
                        // Check the header
                        byte[] headerCheck = new byte[pngHeader.length];
                        dis.readFully(headerCheck);
-                       if(!Arrays.equals(headerCheck, pngHeader)) {
+                       if (!Arrays.equals(headerCheck, pngHeader)) {
                                // Throw an exception
                                String message = l10n("invalidHeader");
                                String title = l10n("invalidHeaderTitle");
-                               throw new DataFilterException(title, title,
-                                       "<p>" + message + "</p>", new 
HTMLNode("p").addChild("#", message));
+                               throw new DataFilterException(title, title, 
"<p>" + message + "</p>", new HTMLNode("p").addChild("#",
+                                       message));
                        }
 
                        ByteArrayOutputStream baos = null;
                        DataOutputStream dos = null;
-                       if(output != null) {
+                       if (output != null) {
                                baos = new ByteArrayOutputStream();
                                dos = new DataOutputStream(baos);
                                output.write(pngHeader);
-                               if(logMINOR)
+                               if (logMINOR)
                                        Logger.minor(this, "Writing the PNG 
header to the output bucket");
                        }
 
@@ -135,149 +127,166 @@
                        boolean hasSeenIEND = false;
                        boolean hasSeenIDAT = false;
                        String lastChunkType = "";
-                       
-                       while(!finished) {
+
+                       while (!finished) {
                                boolean skip = false;
-                               if(baos != null)
+                               if (baos != null)
                                        baos.reset();
                                String chunkTypeString = null;
                                // Length of the chunk
                                byte[] lengthBytes = new byte[4];
                                dis.readFully(lengthBytes);
-                               
-                               int length = ((lengthBytes[0] & 0xff) << 24) + 
((lengthBytes[1] & 0xff) << 16) + ((lengthBytes[2] & 0xff) << 8) + 
(lengthBytes[3] & 0xff);
-                               if(logMINOR)
+
+                               int length = ((lengthBytes[0] & 0xff) << 24) + 
((lengthBytes[1] & 0xff) << 16)
+                                       + ((lengthBytes[2] & 0xff) << 8) + 
(lengthBytes[3] & 0xff);
+                               if (logMINOR)
                                        Logger.minor(this, "length " + length);
-                               if(dos != null)
+                               if (dos != null)
                                        dos.write(lengthBytes);
 
                                // Type of the chunk : Should match [a-zA-Z]{4}
                                dis.readFully(lengthBytes);
                                StringBuilder sb = new StringBuilder();
                                byte[] chunkTypeBytes = new byte[4];
-                               for(int i = 0; i < 4; i++) {
+                               for (int i = 0; i < 4; i++) {
                                        char val = (char) lengthBytes[i];
-                                       if((val >= 65 && val <= 99) || (val >= 
97 && val <= 122)) {
+                                       if ((val >= 65 && val <= 99) || (val >= 
97 && val <= 122)) {
                                                chunkTypeBytes[i] = 
lengthBytes[i];
                                                sb.append(val);
-                                       } else { 
+                                       } else {
                                                String chunkName = 
HexUtil.bytesToHex(lengthBytes, 0, 4);
-                                               throwError("Unknown Chunk"  , 
"The name of the chunk is invalid! (" + chunkName+")");
+                                               throwError("Unknown Chunk", 
"The name of the chunk is invalid! (" + chunkName + ")");
                                        }
                                }
                                chunkTypeString = sb.toString();
-                               if(logMINOR)
+                               if (logMINOR)
                                        Logger.minor(this, "name " + 
chunkTypeString);
 
                                // Content of the chunk
                                byte[] chunkData = new byte[length];
                                dis.readFully(chunkData, 0, length);
-                               if(logMINOR)
-                                       if(logDEBUG)
+                               if (logMINOR)
+                                       if (logDEBUG)
                                                Logger.minor(this, "data " + 
(chunkData.length == 0 ? "null" : HexUtil.bytesToHex(chunkData)));
                                        else
                                                Logger.minor(this, "data " + 
chunkData.length);
-                               if(dos != null)
+                               if (dos != null)
                                        dos.write(chunkTypeBytes);
-                               if(dos != null)
+                               if (dos != null)
                                        dos.write(chunkData);
 
                                // CRC of the chunk
                                byte[] crcLengthBytes = new byte[4];
                                dis.readFully(crcLengthBytes);
-                               if(dos != null)
+                               if (dos != null)
                                        dos.write(crcLengthBytes);
 
-                               if(checkCRCs) {
-                                       long readCRC = (((crcLengthBytes[0] & 
0xff) << 24) + ((crcLengthBytes[1] & 0xff) << 16) + ((crcLengthBytes[2] & 0xff) 
<< 8) + (crcLengthBytes[3] & 0xff)) & 0x00000000ffffffffL;
+                               if (checkCRCs) {
+                                       long readCRC = (((crcLengthBytes[0] & 
0xff) << 24) + ((crcLengthBytes[1] & 0xff) << 16)
+                                               + ((crcLengthBytes[2] & 0xff) 
<< 8) + (crcLengthBytes[3] & 0xff)) & 0x00000000ffffffffL;
                                        CRC32 crc = new CRC32();
                                        crc.update(chunkTypeBytes);
                                        crc.update(chunkData);
                                        long computedCRC = crc.getValue();
-                                       
-                                       if(readCRC != computedCRC) {
+
+                                       if (readCRC != computedCRC) {
                                                skip = true;
-                                               if(logMINOR)
-                                                       Logger.minor(this, "CRC 
of the chunk " + chunkTypeString + " doesn't match (" + 
Long.toHexString(readCRC) + " but should be " + Long.toHexString(computedCRC) + 
")!");
+                                               if (logMINOR)
+                                                       Logger.minor(this, "CRC 
of the chunk " + chunkTypeString + " doesn't match ("
+                                                               + 
Long.toHexString(readCRC) + " but should be " + Long.toHexString(computedCRC)
+                                                               + ")!");
                                        }
                                }
 
                                boolean validChunkType = false;
-                               
-                               if(!skip && "IHDR".equals(chunkTypeString)) {
-                                       if(hasSeenIHDR)
+
+                               if (!skip && "IHDR".equals(chunkTypeString)) {
+                                       if (hasSeenIHDR)
                                                throwError("Duplicate IHDR", 
"Two IHDR chunks detected!!");
                                        hasSeenIHDR = true;
                                        validChunkType = true;
                                }
 
-                               if(!hasSeenIHDR)
+                               if (!hasSeenIHDR)
                                        throwError("No IHDR chunk!", "No IHDR 
chunk!");
 
-                               if(!skip && "IEND".equals(chunkTypeString)) {
-                                       if(hasSeenIEND) // XXX impossible code 
path: it should have throwed as "IEND not last chunk" 
+                               if (!skip && "IEND".equals(chunkTypeString)) {
+                                       if (hasSeenIEND) // XXX impossible code 
path: it should have throwed as "IEND not last chunk" 
                                                throwError("Two IEND chunks 
detected!!", "Two IEND chunks detected!!");
                                        hasSeenIEND = true;
                                        validChunkType = true;
                                }
-                               
-                               if(!skip && 
"PLTE".equalsIgnoreCase(chunkTypeString)) {
-                                       if(hasSeenIDAT)
+
+                               if (!skip && 
"PLTE".equalsIgnoreCase(chunkTypeString)) {
+                                       if (hasSeenIDAT)
                                                throwError("PLTE must be before 
IDAT", "PLTE must be before IDAT");
                                        validChunkType = true;
                                }
-                               
-                               if(!skip && 
"IDAT".equalsIgnoreCase(chunkTypeString)) {
-                                       if(hasSeenIDAT && 
!"IDAT".equalsIgnoreCase(lastChunkType))
-                                               throwError("Multiple IDAT 
chunks must be consecutive!", "Multiple IDAT chunks must be consecutive!");
+
+                               if (!skip && 
"IDAT".equalsIgnoreCase(chunkTypeString)) {
+                                       if (hasSeenIDAT && 
!"IDAT".equalsIgnoreCase(lastChunkType))
+                                               throwError("Multiple IDAT 
chunks must be consecutive!",
+                                                       "Multiple IDAT chunks 
must be consecutive!");
                                        hasSeenIDAT = true;
                                        validChunkType = true;
                                }
-                               
-                               if(!validChunkType) {
-                                       for(int 
i=0;i<HARMLESS_CHUNK_TYPES.length;i++) {
-                                               
if(HARMLESS_CHUNK_TYPES[i].equals(chunkTypeString))
+
+                               if (!validChunkType) {
+                                       for (int i = 0; i < 
HARMLESS_CHUNK_TYPES.length; i++) {
+                                               if 
(HARMLESS_CHUNK_TYPES[i].equals(chunkTypeString))
                                                        validChunkType = true;
                                        }
                                }
-                               
-                               if(dis.available() < 1) {
-                                       if(!(hasSeenIEND && hasSeenIHDR))
+
+                               if (dis.available() < 1) {
+                                       if (!(hasSeenIEND && hasSeenIHDR))
                                                throwError("Missing IEND or 
IHDR!", "Missing IEND or IHDR!");
                                        finished = true;
                                }
 
-                               if("text".equalsIgnoreCase(chunkTypeString) || 
"itxt".equalsIgnoreCase(chunkTypeString)
-                                               || 
"ztxt".equalsIgnoreCase(chunkTypeString)) {
-                                       if(deleteText) skip = true;
-                                       else validChunkType = true;
-                               } else if(deleteTimestamp && 
"time".equalsIgnoreCase(chunkTypeString)) {
-                                       if(deleteTimestamp) skip = true;
-                                       else validChunkType = true;
+                               if ("text".equalsIgnoreCase(chunkTypeString) || 
"itxt".equalsIgnoreCase(chunkTypeString)
+                                       || 
"ztxt".equalsIgnoreCase(chunkTypeString)) {
+                                       if (deleteText)
+                                               skip = true;
+                                       else
+                                               validChunkType = true;
+                               } else if (deleteTimestamp && 
"time".equalsIgnoreCase(chunkTypeString)) {
+                                       if (deleteTimestamp)
+                                               skip = true;
+                                       else
+                                               validChunkType = true;
                                }
-                               
-                               if(!validChunkType) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Skipping 
unknown chunk type "+chunkTypeString);
-                                       if(output == null)
+
+                               if (!validChunkType) {
+                                       if (logMINOR)
+                                               Logger.minor(this, "Skipping 
unknown chunk type " + chunkTypeString);
+                                       if (output == null)
                                                return null;
                                        skip = true;
                                }
-                               
-                               if(skip && output == null)
+
+                               if (skip && output == null)
                                        return null;
-                               else if(!skip && output != null) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Writing " + 
chunkTypeString + " (" + baos.size() + ") to the output bucket");
+                               else if (!skip && output != null) {
+                                       if (logMINOR)
+                                               Logger
+                                                       .minor(this, "Writing " 
+ chunkTypeString + " (" + baos.size()
+                                                               + ") to the 
output bucket");
                                        baos.writeTo(output);
                                        baos.flush();
                                }
                                lastChunkType = chunkTypeString;
                        }
-                       if(hasSeenIEND && dis.available() > 0)
+                       if (hasSeenIEND && dis.available() > 0)
                                throwError("IEND not last chunk", "IEND not 
last chunk");
-                       
+
                        dis.close();
+               } catch (ArrayIndexOutOfBoundsException e) {
+                       throwError("ArrayIndexOutOfBoundsException while 
filtering", "ArrayIndexOutOfBoundsException while filtering");
+               } catch (NegativeArraySizeException e) {
+                       throwError("NegativeArraySizeException while 
filtering", "NegativeArraySizeException while filtering");
+               } catch (EOFException e) {
+                       throwError("EOF Exception while filtering", "EOF 
Exception while filtering");
                } finally {
                        Closer.close(dis);
                        Closer.close(bis);
@@ -304,13 +313,14 @@
                final Bucket out = new FileBucket(fout, false, true, false, 
false, false);
                try {
                        Logger.setupStdoutLogging(Logger.MINOR, "");
-                       ContentFilter.FilterOutput output = 
ContentFilter.filter(data, new ArrayBucketFactory(), "image/png", new 
URI("http://127.0.0.1:8888/";), null);
+                       ContentFilter.FilterOutput output = 
ContentFilter.filter(data, new ArrayBucketFactory(), "image/png",
+                               new URI("http://127.0.0.1:8888/";), null);
                        BucketTools.copy(output.data, out);
-               } catch(IOException e) {
+               } catch (IOException e) {
                        System.out.println("Bucket error?: " + e.getMessage());
-               } catch(URISyntaxException e) {
+               } catch (URISyntaxException e) {
                        System.out.println("Internal error: " + e.getMessage());
-               } catch(InvalidThresholdException e) {
+               } catch (InvalidThresholdException e) {
                } finally {
                        data.free();
                }
@@ -319,14 +329,14 @@
        private void throwError(String shortReason, String reason) throws 
DataFilterException {
                // Throw an exception
                String message = "Invalid PNG";
-               if(reason != null) 
+               if (reason != null)
                        message += ' ' + reason;
-               if(shortReason != null)
+               if (shortReason != null)
                        message += " - " + shortReason;
-               DataFilterException e = new DataFilterException(shortReason, 
shortReason,
-                               "<p>"+message+"</p>", new 
HTMLNode("p").addChild("#", message));
-               if(Logger.shouldLog(Logger.NORMAL, this))
-                       Logger.normal(this, "Throwing "+e, e);
+               DataFilterException e = new DataFilterException(shortReason, 
shortReason, "<p>" + message + "</p>",
+                       new HTMLNode("p").addChild("#", message));
+               if (Logger.shouldLog(Logger.NORMAL, this))
+                       Logger.normal(this, "Throwing " + e, e);
                throw e;
        }
 }

_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs

Reply via email to