Skip to content

Commit 8939acc

Browse files
committed
8358057: Update validation of ICC_Profile header data
Reviewed-by: honkar
1 parent fd0ab04 commit 8939acc

File tree

4 files changed

+135
-38
lines changed

4 files changed

+135
-38
lines changed

src/java.desktop/share/classes/java/awt/color/ICC_Profile.java

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -811,12 +811,12 @@ public static ICC_Profile getInstance(byte[] data) {
811811
}
812812

813813
try {
814-
if (getColorSpaceType(p) == ColorSpace.TYPE_GRAY
814+
if (getColorSpaceType(data) == ColorSpace.TYPE_GRAY
815815
&& getData(p, icSigMediaWhitePointTag) != null
816816
&& getData(p, icSigGrayTRCTag) != null) {
817817
return new ICC_ProfileGray(p);
818818
}
819-
if (getColorSpaceType(p) == ColorSpace.TYPE_RGB
819+
if (getColorSpaceType(data) == ColorSpace.TYPE_RGB
820820
&& getData(p, icSigMediaWhitePointTag) != null
821821
&& getData(p, icSigRedColorantTag) != null
822822
&& getData(p, icSigGreenColorantTag) != null
@@ -1028,13 +1028,8 @@ public int getColorSpaceType() {
10281028
if (info != null) {
10291029
return info.colorSpaceType;
10301030
}
1031-
return getColorSpaceType(cmmProfile());
1032-
}
1033-
1034-
private static int getColorSpaceType(Profile p) {
1035-
byte[] theHeader = getData(p, icSigHead);
1036-
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
1037-
return iccCStoJCS(theColorSpaceSig);
1031+
byte[] theHeader = getData(cmmProfile(), icSigHead);
1032+
return getColorSpaceType(theHeader);
10381033
}
10391034

10401035
private static int getColorSpaceType(byte[] theHeader) {
@@ -1057,8 +1052,7 @@ private static int getColorSpaceType(byte[] theHeader) {
10571052
*/
10581053
public int getPCSType() {
10591054
byte[] theHeader = getData(icSigHead);
1060-
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
1061-
return iccCStoJCS(thePCSSig);
1055+
return getPCSType(theHeader);
10621056
}
10631057

10641058
private static int getPCSType(byte[] theHeader) {
@@ -1189,23 +1183,19 @@ private static void verifyHeader(byte[] data) {
11891183
checkRenderingIntent(data);
11901184
}
11911185

1192-
private static boolean checkRenderingIntent(byte[] header) {
1186+
private static void checkRenderingIntent(byte[] header) {
11931187
int index = ICC_Profile.icHdrRenderingIntent;
1194-
1195-
/* According to ICC spec, only the least-significant 16 bits shall be
1196-
* used to encode the rendering intent. The most significant 16 bits
1197-
* shall be set to zero. Thus, we are ignoring two most significant
1198-
* bytes here. Please refer ICC Spec Document for more details.
1188+
/*
1189+
* ICC spec: only the least-significant 16 bits encode the rendering
1190+
* intent. The most significant 16 bits must be zero and can be ignored.
1191+
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
11991192
*/
1200-
int renderingIntent = ((header[index+2] & 0xff) << 8) |
1201-
(header[index+3] & 0xff);
1202-
1203-
switch (renderingIntent) {
1204-
case icPerceptual, icMediaRelativeColorimetric,
1205-
icSaturation, icAbsoluteColorimetric -> {
1206-
return true;
1207-
}
1208-
default -> throw new IllegalArgumentException("Unknown Rendering Intent");
1193+
// Extract 16-bit unsigned rendering intent (0–65535)
1194+
int intent = (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
1195+
// Only check upper bound since intent can't be negative
1196+
if (intent > icICCAbsoluteColorimetric) {
1197+
throw new IllegalArgumentException(
1198+
"Unknown Rendering Intent: %d".formatted(intent));
12091199
}
12101200
}
12111201

src/java.desktop/share/classes/java/awt/image/ColorConvertOp.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -684,13 +684,10 @@ public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {
684684
private int getRenderingIntent (ICC_Profile profile) {
685685
byte[] header = profile.getData(ICC_Profile.icSigHead);
686686
int index = ICC_Profile.icHdrRenderingIntent;
687-
688-
/* According to ICC spec, only the least-significant 16 bits shall be
689-
* used to encode the rendering intent. The most significant 16 bits
690-
* shall be set to zero. Thus, we are ignoring two most significant
691-
* bytes here.
692-
*
693-
* See https://www.color.org/ICC1v42_2006-05.pdf, section 7.2.15.
687+
/*
688+
* ICC spec: only the least-significant 16 bits encode the rendering
689+
* intent. The most significant 16 bits must be zero and can be ignored.
690+
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
694691
*/
695692
return ((header[index+2] & 0xff) << 8) |
696693
(header[index+3] & 0xff);
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.color.ColorSpace;
25+
import java.awt.color.ICC_Profile;
26+
27+
import static java.awt.color.ICC_Profile.icAbsoluteColorimetric;
28+
import static java.awt.color.ICC_Profile.icICCAbsoluteColorimetric;
29+
import static java.awt.color.ICC_Profile.icMediaRelativeColorimetric;
30+
import static java.awt.color.ICC_Profile.icPerceptual;
31+
import static java.awt.color.ICC_Profile.icRelativeColorimetric;
32+
import static java.awt.color.ICC_Profile.icSaturation;
33+
34+
/**
35+
* @test
36+
* @bug 8358057
37+
* @summary Stress test for ICC_Profile rendering intent parsing and validation
38+
*/
39+
public final class RenderingIntentStressTest {
40+
41+
public static void main(String[] args) {
42+
ICC_Profile builtin = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
43+
ICC_Profile profile = ICC_Profile.getInstance(builtin.getData());
44+
// some random combinations that should be ignored
45+
int[] upperBytes = {0x0000, 0xFFFF, 0xA5A5, 0x8000, 0x0001, 0x8080,
46+
0x0101, 0xAA55, 0x550A, 0xFF00};
47+
for (int up : upperBytes) {
48+
for (int low = 0; low <= 0xFFFF; low++) {
49+
test(profile, up, low);
50+
}
51+
}
52+
}
53+
54+
private static int getRenderingIntent(byte[] header) {
55+
// replicate the logic we have in jdk
56+
int index = ICC_Profile.icHdrRenderingIntent;
57+
return (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
58+
}
59+
60+
private static void test(ICC_Profile profile, int up, int low) {
61+
byte[] header = profile.getData(ICC_Profile.icSigHead);
62+
// These bytes should be ignored
63+
header[ICC_Profile.icHdrRenderingIntent + 0] = (byte) (up >> 8 & 0xFF);
64+
header[ICC_Profile.icHdrRenderingIntent + 1] = (byte) (up & 0xFF);
65+
// This is the actual intent
66+
header[ICC_Profile.icHdrRenderingIntent + 2] = (byte) (low >> 8 & 0xFF);
67+
header[ICC_Profile.icHdrRenderingIntent + 3] = (byte) (low & 0xFF);
68+
69+
boolean isValid = isValidIntent(low);
70+
try {
71+
profile.setData(ICC_Profile.icSigHead, header);
72+
if (!isValid) {
73+
throw new RuntimeException("IAE is expected");
74+
}
75+
} catch (IllegalArgumentException e) {
76+
if (isValid) {
77+
throw e;
78+
}
79+
return;
80+
}
81+
// verify that the intent is correctly stored in the profile by the CMM
82+
byte[] data = profile.getData(ICC_Profile.icSigHead);
83+
int actualIntent = getRenderingIntent(data);
84+
if (actualIntent != low) {
85+
System.out.println("Expected: " + low);
86+
System.out.println("Actual: " + actualIntent);
87+
throw new RuntimeException("Unexpected intent");
88+
}
89+
}
90+
91+
private static boolean isValidIntent(int intent) {
92+
return intent == icPerceptual || intent == icRelativeColorimetric
93+
|| intent == icMediaRelativeColorimetric
94+
|| intent == icSaturation || intent == icAbsoluteColorimetric
95+
|| intent == icICCAbsoluteColorimetric;
96+
}
97+
}

test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8337703
26+
* @bug 8347377 8358057
2727
* @summary To verify if ICC_Profile's setData() and getInstance() methods
2828
* validate header data and throw IAE for invalid values.
2929
* @run main ValidateICCHeaderData
@@ -144,9 +144,7 @@ public static void main(String[] args) throws Exception {
144144
System.out.println("CASE 10: Passed \n");
145145

146146
System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
147-
//valid rendering intent values are 0-3
148-
int invalidRenderIntent = 5;
149-
testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
147+
testInvalidIntent();
150148
System.out.println("CASE 11: Passed \n");
151149

152150
System.out.println("CASE 12: Testing INVALID Header Size ...");
@@ -187,6 +185,21 @@ private static void testInvalidHeaderData(int invalidData, int startIndex,
187185
}
188186
}
189187

188+
private static void testInvalidIntent() {
189+
//valid rendering intent values are 0-3
190+
int invalidRenderIntent = 5;
191+
try {
192+
setTag(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
193+
throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
194+
} catch (IllegalArgumentException iae) {
195+
String message = iae.getMessage();
196+
System.out.println("Expected IAE thrown: " + message);
197+
if (!message.contains(": " + invalidRenderIntent)) {
198+
throw new RuntimeException("Test Failed ! Unexpected text");
199+
}
200+
}
201+
}
202+
190203
private static void setTag(int value, int startIndex, int fieldLength) {
191204
byte[] byteArray;
192205
if (startIndex == RENDER_INTENT_START_INDEX) {

0 commit comments

Comments
 (0)