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 {
|
try {
|
||||||
if (getColorSpaceType(p) == ColorSpace.TYPE_GRAY
|
if (getColorSpaceType(data) == ColorSpace.TYPE_GRAY
|
||||||
&& getData(p, icSigMediaWhitePointTag) != null
|
&& getData(p, icSigMediaWhitePointTag) != null
|
||||||
&& getData(p, icSigGrayTRCTag) != null) {
|
&& getData(p, icSigGrayTRCTag) != null) {
|
||||||
return new ICC_ProfileGray(p);
|
return new ICC_ProfileGray(p);
|
||||||
}
|
}
|
||||||
if (getColorSpaceType(p) == ColorSpace.TYPE_RGB
|
if (getColorSpaceType(data) == ColorSpace.TYPE_RGB
|
||||||
&& getData(p, icSigMediaWhitePointTag) != null
|
&& getData(p, icSigMediaWhitePointTag) != null
|
||||||
&& getData(p, icSigRedColorantTag) != null
|
&& getData(p, icSigRedColorantTag) != null
|
||||||
&& getData(p, icSigGreenColorantTag) != null
|
&& getData(p, icSigGreenColorantTag) != null
|
||||||
@ -1028,13 +1028,8 @@ public sealed class ICC_Profile implements Serializable
|
|||||||
if (info != null) {
|
if (info != null) {
|
||||||
return info.colorSpaceType;
|
return info.colorSpaceType;
|
||||||
}
|
}
|
||||||
return getColorSpaceType(cmmProfile());
|
byte[] theHeader = getData(cmmProfile(), icSigHead);
|
||||||
}
|
return getColorSpaceType(theHeader);
|
||||||
|
|
||||||
private static int getColorSpaceType(Profile p) {
|
|
||||||
byte[] theHeader = getData(p, icSigHead);
|
|
||||||
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
|
|
||||||
return iccCStoJCS(theColorSpaceSig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getColorSpaceType(byte[] theHeader) {
|
private static int getColorSpaceType(byte[] theHeader) {
|
||||||
@ -1057,8 +1052,7 @@ public sealed class ICC_Profile implements Serializable
|
|||||||
*/
|
*/
|
||||||
public int getPCSType() {
|
public int getPCSType() {
|
||||||
byte[] theHeader = getData(icSigHead);
|
byte[] theHeader = getData(icSigHead);
|
||||||
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
|
return getPCSType(theHeader);
|
||||||
return iccCStoJCS(thePCSSig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getPCSType(byte[] theHeader) {
|
private static int getPCSType(byte[] theHeader) {
|
||||||
@ -1189,23 +1183,19 @@ public sealed class ICC_Profile implements Serializable
|
|||||||
checkRenderingIntent(data);
|
checkRenderingIntent(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkRenderingIntent(byte[] header) {
|
private static void checkRenderingIntent(byte[] header) {
|
||||||
int index = ICC_Profile.icHdrRenderingIntent;
|
int index = ICC_Profile.icHdrRenderingIntent;
|
||||||
|
/*
|
||||||
/* According to ICC spec, only the least-significant 16 bits shall be
|
* ICC spec: only the least-significant 16 bits encode the rendering
|
||||||
* used to encode the rendering intent. The most significant 16 bits
|
* intent. The most significant 16 bits must be zero and can be ignored.
|
||||||
* shall be set to zero. Thus, we are ignoring two most significant
|
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
|
||||||
* bytes here. Please refer ICC Spec Document for more details.
|
|
||||||
*/
|
*/
|
||||||
int renderingIntent = ((header[index+2] & 0xff) << 8) |
|
// Extract 16-bit unsigned rendering intent (0–65535)
|
||||||
(header[index+3] & 0xff);
|
int intent = (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
|
||||||
|
// Only check upper bound since intent can't be negative
|
||||||
switch (renderingIntent) {
|
if (intent > icICCAbsoluteColorimetric) {
|
||||||
case icPerceptual, icMediaRelativeColorimetric,
|
throw new IllegalArgumentException(
|
||||||
icSaturation, icAbsoluteColorimetric -> {
|
"Unknown Rendering Intent: %d".formatted(intent));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default -> throw new IllegalArgumentException("Unknown Rendering 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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) {
|
private int getRenderingIntent (ICC_Profile profile) {
|
||||||
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
||||||
int index = ICC_Profile.icHdrRenderingIntent;
|
int index = ICC_Profile.icHdrRenderingIntent;
|
||||||
|
/*
|
||||||
/* According to ICC spec, only the least-significant 16 bits shall be
|
* ICC spec: only the least-significant 16 bits encode the rendering
|
||||||
* used to encode the rendering intent. The most significant 16 bits
|
* intent. The most significant 16 bits must be zero and can be ignored.
|
||||||
* shall be set to zero. Thus, we are ignoring two most significant
|
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
|
||||||
* bytes here.
|
|
||||||
*
|
|
||||||
* See https://www.color.org/ICC1v42_2006-05.pdf, section 7.2.15.
|
|
||||||
*/
|
*/
|
||||||
return ((header[index+2] & 0xff) << 8) |
|
return ((header[index+2] & 0xff) << 8) |
|
||||||
(header[index+3] & 0xff);
|
(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
|
* @test
|
||||||
* @bug 8337703
|
* @bug 8347377 8358057
|
||||||
* @summary To verify if ICC_Profile's setData() and getInstance() methods
|
* @summary To verify if ICC_Profile's setData() and getInstance() methods
|
||||||
* validate header data and throw IAE for invalid values.
|
* validate header data and throw IAE for invalid values.
|
||||||
* @run main ValidateICCHeaderData
|
* @run main ValidateICCHeaderData
|
||||||
@ -144,9 +144,7 @@ public class ValidateICCHeaderData {
|
|||||||
System.out.println("CASE 10: Passed \n");
|
System.out.println("CASE 10: Passed \n");
|
||||||
|
|
||||||
System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
|
System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
|
||||||
//valid rendering intent values are 0-3
|
testInvalidIntent();
|
||||||
int invalidRenderIntent = 5;
|
|
||||||
testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
|
|
||||||
System.out.println("CASE 11: Passed \n");
|
System.out.println("CASE 11: Passed \n");
|
||||||
|
|
||||||
System.out.println("CASE 12: Testing INVALID Header Size ...");
|
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) {
|
private static void setTag(int value, int startIndex, int fieldLength) {
|
||||||
byte[] byteArray;
|
byte[] byteArray;
|
||||||
if (startIndex == RENDER_INTENT_START_INDEX) {
|
if (startIndex == RENDER_INTENT_START_INDEX) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user