jdk/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java
Harshitha Onkar c71be802b5 8361748: Enforce limits on the size of an XBM image
Reviewed-by: prr, jdv
2025-08-07 21:19:47 +00:00

208 lines
8.0 KiB
Java

/*
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Reads xbitmap format images into a DIBitmap structure.
*/
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 static java.lang.Math.multiplyExact;
/**
* Parse files of the form:
*
* #define foo_width w
* #define foo_height h
* static char foo_bits[] = {
* 0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,
* 0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,0xnn,
* 0xnn,0xnn,0xnn,0xnn};
*
* @author James Gosling
*/
public class XbmImageDecoder extends ImageDecoder {
private static byte[] XbmColormap = {(byte) 255, (byte) 255, (byte) 255,
0, 0, 0};
private static int XbmHints = (ImageConsumer.TOPDOWNLEFTRIGHT |
ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEPASS |
ImageConsumer.SINGLEFRAME);
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)) {
// If the topmost stream is a metered stream,
// we take forever to decode the image...
input = new BufferedInputStream(input, 80);
}
}
/**
* An error has occurred. Throw an exception.
*/
private static void error(String s1) throws ImageFormatException {
throw new ImageFormatException(s1);
}
/**
* produce an image from the stream.
*/
public void produceImage() throws IOException, ImageFormatException {
int H = 0;
int W = 0;
int x = 0;
int y = 0;
int n = 0;
int state = 0;
byte[] raster = null;
IndexColorModel model = null;
String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]";
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 (!token[2].isBlank() && state == 0) {
W = Integer.parseInt(token[2]);
state = 1; // after width is set
} else if (!token[2].isBlank() && state == 1) {
H = Integer.parseInt(token[2]);
state = 2; // after height is set
}
} 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;
}
}
if (state != 3) {
error("Width or Height of XBM file not defined");
}
// loop to process image data
while (!aborted && (line = br.readLine()) != null) {
lineNum++;
if (line.contains("[]")) {
Matcher matcher = Pattern.compile(matchRegex).matcher(line);
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 = line.substring(startIndex, endIndex);
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);
}
}
}