8373727: New XBM images parser regression: only the first line of the bitmap array is parsed

Reviewed-by: prr
Backport-of: 7f707ba8e746d859ac171d71ef8f731953a92e6a
This commit is contained in:
Damon Nguyen 2026-01-14 17:03:15 +00:00
parent ffc6d1b74b
commit d011d7c7cc
6 changed files with 128 additions and 59 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2026, 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
@ -92,8 +92,8 @@ public class XbmImageDecoder extends ImageDecoder {
byte[] raster = null;
IndexColorModel model = null;
String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]";
String replaceRegex = "(0[xX])|,|[\\s+]|[};]";
String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)";
String replaceRegex = "0[xX]|,|\\s+|\\};";
String line;
int lineNum = 0;
@ -111,11 +111,19 @@ public class XbmImageDecoder extends ImageDecoder {
}
try {
if (!token[2].isBlank() && state == 0) {
W = Integer.parseInt(token[2]);
state = 1; // after width is set
if (token[1].endsWith("th")) {
W = Integer.parseInt(token[2]);
} else if (token[1].endsWith("t")) {
H = Integer.parseInt(token[2]);
}
state = 1; // after first dimension is set
} else if (!token[2].isBlank() && state == 1) {
H = Integer.parseInt(token[2]);
state = 2; // after height is set
if (token[1].endsWith("th")) {
W = Integer.parseInt(token[2]);
} else if (token[1].endsWith("t")) {
H = Integer.parseInt(token[2]);
}
state = 2; // after second dimension is set
}
} catch (NumberFormatException nfe) {
// parseInt() can throw NFE
@ -147,59 +155,81 @@ public class XbmImageDecoder extends ImageDecoder {
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 (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++;
}
if (!contFlag) {
if (line.contains("[]")) {
contFlag = true;
} else {
continue;
}
}
int end = line.indexOf(';');
if (end >= 0) {
sb.append(line, 0, end + 1);
break;
} else {
sb.append(line).append(System.lineSeparator());
}
}
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);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2026, 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
@ -29,10 +29,14 @@
* @run main XBMDecoderTest
*/
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintStream;
import java.util.Arrays;
import javax.swing.ImageIcon;
public class XBMDecoderTest {
@ -57,21 +61,39 @@ public class XBMDecoderTest {
ImageIcon icon = new ImageIcon(fis.readAllBytes());
boolean isErrEmpty = errContent.toString().isEmpty();
if (!isErrEmpty) {
System.out.println("Expected ImageFormatException occurred.");
System.out.print(errContent);
}
if (validCase && !isErrEmpty) {
throw new RuntimeException("Test failed: Error stream not empty");
} else if (!validCase && isErrEmpty) {
} else if (!validCase && isErrEmpty && hasPixelData(icon.getImage())) {
throw new RuntimeException("Test failed: ImageFormatException"
+ " expected but not thrown");
}
if (validCase && !hasPixelData(icon.getImage())) {
throw new RuntimeException("Test failed: the parsed image " +
"does not contain any pixel data");
}
System.out.println("PASSED\n");
} finally {
System.setErr(originalErr);
}
}
}
private static boolean hasPixelData(Image img) {
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
int[] pixels = bi.getRGB(0, 0, w, h, null, 0, w);
if (Arrays.stream(pixels).allMatch(i -> i == 0)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,6 @@
#define test_width 16
#define test_height 3
#define ht_x 1
#define ht_y 2
static unsigned char test_bits[] = {
};

View File

@ -1,3 +1,3 @@
#define k_wt 16
#define k_ht 1
#define k_width 16
#define k_height 1
k[] = { 0x10, 1234567890};

View File

@ -0,0 +1,3 @@
#define test_width 16
#define test_height 2
static unsigned char test_bits[] = { 0x13, 0x11, 0xAB+, 0xff };

View File

@ -0,0 +1,8 @@
#define test_width 16
#define test_height 3
#define ht_x 1
#define ht_y 2
static unsigned char test_bits[] = {
0x20, 0x10,
0x25, 0x01,
0xAC, 0xab };