8358057: Update validation of ICC_Profile header data
Reviewed-by: honkar
This commit is contained in:
parent
fd0ab04367
commit
8939acc8ab
@ -811,12 +811,12 @@ public sealed class ICC_Profile implements Serializable
|
||||
}
|
||||
|
||||
try {
|
||||
if (getColorSpaceType(p) == ColorSpace.TYPE_GRAY
|
||||
if (getColorSpaceType(data) == ColorSpace.TYPE_GRAY
|
||||
&& getData(p, icSigMediaWhitePointTag) != null
|
||||
&& getData(p, icSigGrayTRCTag) != null) {
|
||||
return new ICC_ProfileGray(p);
|
||||
}
|
||||
if (getColorSpaceType(p) == ColorSpace.TYPE_RGB
|
||||
if (getColorSpaceType(data) == ColorSpace.TYPE_RGB
|
||||
&& getData(p, icSigMediaWhitePointTag) != null
|
||||
&& getData(p, icSigRedColorantTag) != null
|
||||
&& getData(p, icSigGreenColorantTag) != null
|
||||
@ -1028,13 +1028,8 @@ public sealed class ICC_Profile implements Serializable
|
||||
if (info != null) {
|
||||
return info.colorSpaceType;
|
||||
}
|
||||
return getColorSpaceType(cmmProfile());
|
||||
}
|
||||
|
||||
private static int getColorSpaceType(Profile p) {
|
||||
byte[] theHeader = getData(p, icSigHead);
|
||||
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
|
||||
return iccCStoJCS(theColorSpaceSig);
|
||||
byte[] theHeader = getData(cmmProfile(), icSigHead);
|
||||
return getColorSpaceType(theHeader);
|
||||
}
|
||||
|
||||
private static int getColorSpaceType(byte[] theHeader) {
|
||||
@ -1057,8 +1052,7 @@ public sealed class ICC_Profile implements Serializable
|
||||
*/
|
||||
public int getPCSType() {
|
||||
byte[] theHeader = getData(icSigHead);
|
||||
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
|
||||
return iccCStoJCS(thePCSSig);
|
||||
return getPCSType(theHeader);
|
||||
}
|
||||
|
||||
private static int getPCSType(byte[] theHeader) {
|
||||
@ -1189,23 +1183,19 @@ public sealed class ICC_Profile implements Serializable
|
||||
checkRenderingIntent(data);
|
||||
}
|
||||
|
||||
private static boolean checkRenderingIntent(byte[] header) {
|
||||
private static void checkRenderingIntent(byte[] header) {
|
||||
int index = ICC_Profile.icHdrRenderingIntent;
|
||||
|
||||
/* According to ICC spec, only the least-significant 16 bits shall be
|
||||
* used to encode the rendering intent. The most significant 16 bits
|
||||
* shall be set to zero. Thus, we are ignoring two most significant
|
||||
* bytes here. Please refer ICC Spec Document for more details.
|
||||
/*
|
||||
* ICC spec: only the least-significant 16 bits encode the rendering
|
||||
* intent. The most significant 16 bits must be zero and can be ignored.
|
||||
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
|
||||
*/
|
||||
int renderingIntent = ((header[index+2] & 0xff) << 8) |
|
||||
(header[index+3] & 0xff);
|
||||
|
||||
switch (renderingIntent) {
|
||||
case icPerceptual, icMediaRelativeColorimetric,
|
||||
icSaturation, icAbsoluteColorimetric -> {
|
||||
return true;
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown Rendering Intent");
|
||||
// Extract 16-bit unsigned rendering intent (0–65535)
|
||||
int intent = (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
|
||||
// Only check upper bound since intent can't be negative
|
||||
if (intent > icICCAbsoluteColorimetric) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown Rendering Intent: %d".formatted(intent));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 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
|
||||
@ -684,13 +684,10 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp {
|
||||
private int getRenderingIntent (ICC_Profile profile) {
|
||||
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
||||
int index = ICC_Profile.icHdrRenderingIntent;
|
||||
|
||||
/* According to ICC spec, only the least-significant 16 bits shall be
|
||||
* used to encode the rendering intent. The most significant 16 bits
|
||||
* shall be set to zero. Thus, we are ignoring two most significant
|
||||
* bytes here.
|
||||
*
|
||||
* See https://www.color.org/ICC1v42_2006-05.pdf, section 7.2.15.
|
||||
/*
|
||||
* ICC spec: only the least-significant 16 bits encode the rendering
|
||||
* intent. The most significant 16 bits must be zero and can be ignored.
|
||||
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
|
||||
*/
|
||||
return ((header[index+2] & 0xff) << 8) |
|
||||
(header[index+3] & 0xff);
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
|
||||
import static java.awt.color.ICC_Profile.icAbsoluteColorimetric;
|
||||
import static java.awt.color.ICC_Profile.icICCAbsoluteColorimetric;
|
||||
import static java.awt.color.ICC_Profile.icMediaRelativeColorimetric;
|
||||
import static java.awt.color.ICC_Profile.icPerceptual;
|
||||
import static java.awt.color.ICC_Profile.icRelativeColorimetric;
|
||||
import static java.awt.color.ICC_Profile.icSaturation;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8358057
|
||||
* @summary Stress test for ICC_Profile rendering intent parsing and validation
|
||||
*/
|
||||
public final class RenderingIntentStressTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ICC_Profile builtin = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile = ICC_Profile.getInstance(builtin.getData());
|
||||
// some random combinations that should be ignored
|
||||
int[] upperBytes = {0x0000, 0xFFFF, 0xA5A5, 0x8000, 0x0001, 0x8080,
|
||||
0x0101, 0xAA55, 0x550A, 0xFF00};
|
||||
for (int up : upperBytes) {
|
||||
for (int low = 0; low <= 0xFFFF; low++) {
|
||||
test(profile, up, low);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getRenderingIntent(byte[] header) {
|
||||
// replicate the logic we have in jdk
|
||||
int index = ICC_Profile.icHdrRenderingIntent;
|
||||
return (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
|
||||
}
|
||||
|
||||
private static void test(ICC_Profile profile, int up, int low) {
|
||||
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
||||
// These bytes should be ignored
|
||||
header[ICC_Profile.icHdrRenderingIntent + 0] = (byte) (up >> 8 & 0xFF);
|
||||
header[ICC_Profile.icHdrRenderingIntent + 1] = (byte) (up & 0xFF);
|
||||
// This is the actual intent
|
||||
header[ICC_Profile.icHdrRenderingIntent + 2] = (byte) (low >> 8 & 0xFF);
|
||||
header[ICC_Profile.icHdrRenderingIntent + 3] = (byte) (low & 0xFF);
|
||||
|
||||
boolean isValid = isValidIntent(low);
|
||||
try {
|
||||
profile.setData(ICC_Profile.icSigHead, header);
|
||||
if (!isValid) {
|
||||
throw new RuntimeException("IAE is expected");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (isValid) {
|
||||
throw e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// verify that the intent is correctly stored in the profile by the CMM
|
||||
byte[] data = profile.getData(ICC_Profile.icSigHead);
|
||||
int actualIntent = getRenderingIntent(data);
|
||||
if (actualIntent != low) {
|
||||
System.out.println("Expected: " + low);
|
||||
System.out.println("Actual: " + actualIntent);
|
||||
throw new RuntimeException("Unexpected intent");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidIntent(int intent) {
|
||||
return intent == icPerceptual || intent == icRelativeColorimetric
|
||||
|| intent == icMediaRelativeColorimetric
|
||||
|| intent == icSaturation || intent == icAbsoluteColorimetric
|
||||
|| intent == icICCAbsoluteColorimetric;
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8337703
|
||||
* @bug 8347377 8358057
|
||||
* @summary To verify if ICC_Profile's setData() and getInstance() methods
|
||||
* validate header data and throw IAE for invalid values.
|
||||
* @run main ValidateICCHeaderData
|
||||
@ -144,9 +144,7 @@ public class ValidateICCHeaderData {
|
||||
System.out.println("CASE 10: Passed \n");
|
||||
|
||||
System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
|
||||
//valid rendering intent values are 0-3
|
||||
int invalidRenderIntent = 5;
|
||||
testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
|
||||
testInvalidIntent();
|
||||
System.out.println("CASE 11: Passed \n");
|
||||
|
||||
System.out.println("CASE 12: Testing INVALID Header Size ...");
|
||||
@ -187,6 +185,21 @@ public class ValidateICCHeaderData {
|
||||
}
|
||||
}
|
||||
|
||||
private static void testInvalidIntent() {
|
||||
//valid rendering intent values are 0-3
|
||||
int invalidRenderIntent = 5;
|
||||
try {
|
||||
setTag(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
|
||||
throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
String message = iae.getMessage();
|
||||
System.out.println("Expected IAE thrown: " + message);
|
||||
if (!message.contains(": " + invalidRenderIntent)) {
|
||||
throw new RuntimeException("Test Failed ! Unexpected text");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setTag(int value, int startIndex, int fieldLength) {
|
||||
byte[] byteArray;
|
||||
if (startIndex == RENDER_INTENT_START_INDEX) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user