diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index 1b7cd41daa9..4e79c6a6354 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java @@ -31,12 +31,8 @@ package sun.awt.image; import java.awt.image.ImageConsumer; import java.awt.image.IndexColorModel; import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.io.IOException; import static java.lang.Math.multiplyExact; @@ -62,9 +58,8 @@ public class XbmImageDecoder extends ImageDecoder { ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); + private static final int MAX_CHAR_LIMIT = 128000; private static final int MAX_XBM_SIZE = 16384; - private static final int HEADER_SCAN_LIMIT = 100; - public XbmImageDecoder(InputStreamImageSource src, InputStream is) { super(src, is); if (!(input instanceof BufferedInputStream)) { @@ -86,150 +81,229 @@ public class XbmImageDecoder extends ImageDecoder { * produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { + char[] nm = new char[80]; + int c; + int i = 0; + int state = 0; int H = 0; int W = 0; int x = 0; int y = 0; - int n = 0; - int state = 0; + boolean consumeWidthValue = false; + boolean consumeHeightValue = false; + boolean validWidthConsumed = false; + boolean validHeightConsumed = false; + // number of tokens seen as part of this define statement + int defineTokenCount = 0; byte[] raster = null; IndexColorModel model = null; + int charCount = 0; - String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)"; - String replaceRegex = "0[xX]|,|\\s+|\\};"; - - String line; - int lineNum = 0; - - try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) { - // loop to process XBM header - width, height and create raster - while (!aborted && (line = br.readLine()) != null - && lineNum <= HEADER_SCAN_LIMIT) { - lineNum++; - // process #define stmts - if (line.trim().startsWith("#define")) { - String[] token = line.split("\\s+"); - if (token.length != 3) { - error("Error while parsing define statement"); - } - try { - if (state < 2) { - if (token[1].endsWith("h")) { - W = Integer.parseInt(token[2]); - } else if (token[1].endsWith("ht")) { - H = Integer.parseInt(token[2]); - } - // After the 1st dimension is set, state becomes 1; - // after the 2nd dimension is set, state becomes 2 - ++state; - } - } catch (NumberFormatException nfe) { - // parseInt() can throw NFE - error("Error while parsing width or height."); - } - } - - if (state == 2) { - if (W <= 0 || H <= 0) { - error("Invalid values for width or height."); - } - if (multiplyExact(W, H) > MAX_XBM_SIZE) { - error("Large XBM file size." - + " Maximum allowed size: " + MAX_XBM_SIZE); - } - model = new IndexColorModel(8, 2, XbmColormap, - 0, false, 0); - setDimensions(W, H); - setColorModel(model); - setHints(XbmHints); - headerComplete(); - raster = new byte[W]; - state = 3; - break; - } + //read header info + while (!aborted && (c = input.read()) != -1) { + charCount++; + if (charCount > MAX_CHAR_LIMIT) { + error("Incomplete image after reading " + + "the maximum allowed number of characters: " + + MAX_CHAR_LIMIT); } - - if (state != 3) { - error("Width or Height of XBM file not defined"); - } - - boolean contFlag = false; - StringBuilder sb = new StringBuilder(); - - // loop to process image data - while (!aborted && (line = br.readLine()) != null) { - lineNum++; - - if (!contFlag) { - if (line.contains("[]")) { - contFlag = true; - } else { + if ('a' <= c && c <= 'z' || + 'A' <= c && c <= 'Z' || + '0' <= c && c <= '9' || c == '#' || c == '_') { + if (i < nm.length) { + nm[i++] = (char) c; + } else { + error("XBM header contains literal greater than size 80"); + } + } else if (i > 0) { + int nc = i; + i = 0; + if (defineTokenCount >= 1) { + // we are inside a #define line + defineTokenCount++; + } + if (defineTokenCount == 0) { + if (nc == 7 && + nm[0] == '#' && + nm[1] == 'd' && + nm[2] == 'e' && + nm[3] == 'f' && + nm[4] == 'i' && + nm[5] == 'n' && + nm[6] == 'e') + { + defineTokenCount++; continue; } - } + } else if (defineTokenCount == 2) { + // consume second token in #define line + if (state < 2) { + if (nm[nc - 1] == 'h' && + !validWidthConsumed) { + consumeWidthValue = true; + } else if ((nm[nc - 1] == 't' && nc > 1 && + nm[nc - 2] == 'h') && + !validHeightConsumed) { + consumeHeightValue = true; + } + } + } else if (defineTokenCount == 3) { + defineTokenCount = 0; + // consume third token in #define line + int n = 0; + for (int p = 0; p < nc; p++) { + if ('0' <= (c = nm[p]) && c <= '9') { + n = n * 10 + c - '0'; + if (n > MAX_XBM_SIZE) { + error("Width/Height cannot be more than: " + + MAX_XBM_SIZE); + } + } else { + error("Invalid width/height value"); + } + } - int end = line.indexOf(';'); - if (end >= 0) { - sb.append(line, 0, end + 1); - break; - } else { - sb.append(line).append(System.lineSeparator()); + if (n > 0 && (consumeWidthValue || consumeHeightValue)) { + if (consumeWidthValue) { + if (!validWidthConsumed) { + W = n; + validWidthConsumed = true; + state++; + } + consumeWidthValue = false; + } else if (consumeHeightValue) { + if (!validHeightConsumed) { + H = n; + validHeightConsumed = true; + state++; + } + consumeHeightValue = false; + } + } + // verify the consumed width & height value and initialize + // required constructs + if (state == 2) { + if (multiplyExact(W, H) > MAX_XBM_SIZE) { + error("Large XBM file size." + + " Maximum allowed size: " + MAX_XBM_SIZE); + } + model = new IndexColorModel(8, 2, XbmColormap, + 0, false, 0); + setDimensions(W, H); + setColorModel(model); + setHints(XbmHints); + headerComplete(); + raster = new byte[W]; + state = 3; + break; + } } } - - String resultLine = sb.toString(); - int cutOffIndex = resultLine.indexOf('{'); - resultLine = resultLine.substring(cutOffIndex + 1); - - Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine); - while (matcher.find()) { - if (y >= H) { - error("Scan size of XBM file exceeds" - + " the defined width x height"); - } - - int startIndex = matcher.start(); - int endIndex = matcher.end(); - String hexByte = resultLine.substring(startIndex, endIndex); - hexByte = hexByte.replaceAll("^\\s+", ""); - - if (!(hexByte.startsWith("0x") - || hexByte.startsWith("0X"))) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - hexByte = hexByte.replaceAll(replaceRegex, ""); - if (hexByte.length() != 2) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - - try { - n = Integer.parseInt(hexByte, 16); - } catch (NumberFormatException nfe) { - error("Error parsing hexadecimal at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - for (int mask = 1; mask <= 0x80; mask <<= 1) { - if (x < W) { - if ((n & mask) != 0) - raster[x] = 1; - else - raster[x] = 0; - } - x++; - } - - if (x >= W) { - int result = setPixels(0, y, W, 1, model, raster, 0, W); - if (result <= 0) { - error("Unexpected error occurred during setPixel()"); - } - x = 0; - y++; - } - } - imageComplete(ImageConsumer.STATICIMAGEDONE, true); } + + if (state != 3) { + error("Width or Height of XBM file not defined"); + } + + // skip until we find '{' + boolean imageDataStarted = false; + while (!aborted && (c = input.read()) != -1) { + charCount++; + if (charCount > MAX_CHAR_LIMIT) { + error("Incomplete image after reading " + + "the maximum allowed number of characters: " + + MAX_CHAR_LIMIT); + } + if (c == '{') { + imageDataStarted = true; + break; + } + } + + if (!imageDataStarted) { + error("Missing '{' at the start of image data"); + } + + // used to make sure that we have the final delimiter '};', + // while parsing the image data + int previousChar = '{'; + // parse image data + boolean imageDataTerminated = false; + while (!aborted && (c = input.read()) != -1) { + charCount++; + if (charCount > MAX_CHAR_LIMIT) { + error("Incomplete image after reading " + + "the maximum allowed number of characters: " + + MAX_CHAR_LIMIT); + } + + if (c == ';') { + if (previousChar != '}') { + error("Abrupt end of image data without '};' delimiter"); + } + imageDataTerminated = true; + break; + } + if (!Character.isWhitespace(c)) { + previousChar = c; + } + + if (',' != c && '}' != c && + !Character.isWhitespace(c)) { + nm[i++] = (char) c; + if (i > 4) { + error("Image hex data should be 3 or 4 characters long"); + } + } else if (i == 3 || i == 4) { + // consume valid hex image data + int n = 0; + int nc = i; + i = 0; + if (nm[0] == '0' && + (nm[1] == 'x' || nm[1] == 'X')) { + for (int p = 2; p < nc; p++) { + c = nm[p]; + if ('0' <= c && c <= '9') + c = c - '0'; + else if ('A' <= c && c <= 'F') + c = c - 'A' + 10; + else if ('a' <= c && c <= 'f') + c = c - 'a' + 10; + else + error("Corrupt hex image data"); + n = n * 16 + c; + } + for (int mask = 1; mask <= 0x80; mask <<= 1) { + if (x < W) { + if ((n & mask) != 0) + raster[x] = 1; + else + raster[x] = 0; + } + x++; + } + if (x >= W) { + if ((y + 1) > H) { + error("Scan size of XBM file exceeds" + + " the defined width x height"); + } + if (setPixels(0, y, W, 1, model, raster, 0, W) == 0) { + error("Unexpected error occurred during setPixel()"); + } + x = 0; + y++; + } + } else { + error("Corrupt hex image data"); + } + } else if (i == 1 || i == 2) { + error("Image hex data should be 3 or 4 characters long"); + } + } + if (!imageDataTerminated) { + error("Missing terminator ';'"); + } + input.close(); + imageComplete(ImageConsumer.STATICIMAGEDONE, true); } }