8264666: Change implementation of safeAdd/safeMult in the LCMSImageLayout class

Reviewed-by: prr
This commit is contained in:
Sergey Bylokhov 2022-04-29 05:53:31 +00:00
parent 1e28fcbc5f
commit 40f19c014f
2 changed files with 180 additions and 26 deletions
src/java.desktop/share/classes/sun/java2d/cmm/lcms
test/jdk/sun/java2d/cmm/ColorConvertOp

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2022, 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
@ -341,44 +341,30 @@ final class LCMSImageLayout {
}
private void verify() throws ImageLayoutException {
if (offset < 0 || offset >= dataArrayLength) {
throw new ImageLayoutException("Invalid image layout");
}
checkIndex(offset, dataArrayLength);
if (nextPixelOffset != getBytesPerPixel(pixelType)) {
throw new ImageLayoutException("Invalid image layout");
}
int lastScanOffset = safeMult(nextRowOffset, (height - 1));
int lastPixelOffset = safeMult(nextPixelOffset, (width -1 ));
long off = (long) offset + lastPixelOffset + lastScanOffset;
lastPixelOffset = safeAdd(lastPixelOffset, lastScanOffset);
int off = safeAdd(offset, lastPixelOffset);
if (off < 0 || off >= dataArrayLength) {
throw new ImageLayoutException("Invalid image layout");
}
checkIndex(off, dataArrayLength);
}
static int safeAdd(int a, int b) throws ImageLayoutException {
long res = a;
res += b;
if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {
private static int checkIndex(long index, int length)
throws ImageLayoutException
{
if (index < 0 || index >= length) {
throw new ImageLayoutException("Invalid image layout");
}
return (int)res;
return (int) index;
}
static int safeMult(int a, int b) throws ImageLayoutException {
long res = a;
res *= b;
if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {
throw new ImageLayoutException("Invalid image layout");
}
return (int)res;
private static int safeMult(int a, int b) throws ImageLayoutException {
long res = (long) a * b;
return checkIndex(res, Integer.MAX_VALUE);
}
@SuppressWarnings("serial") // JDK-implementation class

@ -0,0 +1,168 @@
/*
* 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.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.util.concurrent.atomic.AtomicInteger;
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE;
import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY;
import static java.awt.image.BufferedImage.TYPE_BYTE_GRAY;
import static java.awt.image.BufferedImage.TYPE_BYTE_INDEXED;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE;
import static java.awt.image.BufferedImage.TYPE_INT_BGR;
import static java.awt.image.BufferedImage.TYPE_INT_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_555_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_565_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_GRAY;
/**
* @test
* @bug 8264666
* @summary No exception or errors should occur in ColorConvertOp.filter().
*/
public final class UnexpectedSourceImageSize {
private static final int SIZE = 100;
private static final int[] TYPES = {
TYPE_INT_RGB, TYPE_INT_ARGB, TYPE_INT_ARGB_PRE, TYPE_INT_BGR,
TYPE_3BYTE_BGR, TYPE_4BYTE_ABGR, TYPE_4BYTE_ABGR_PRE,
TYPE_USHORT_565_RGB, TYPE_USHORT_555_RGB, TYPE_BYTE_GRAY,
TYPE_USHORT_GRAY, TYPE_BYTE_BINARY, TYPE_BYTE_INDEXED
};
private static final int[] INTERESTING_POINTS = new int[]{
Integer.MIN_VALUE / SIZE - 1,
-SIZE, -3, -1, 0, 1, 3,
Integer.MAX_VALUE / SIZE + 1,
};
private static final int[] CSs = new int[]{
ColorSpace.CS_sRGB, ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_CIEXYZ,
ColorSpace.CS_PYCC, ColorSpace.CS_GRAY
};
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[CSs.length];
for (int i = 0; i < threads.length; i++) {
ColorSpace cs = ColorSpace.getInstance(CSs[i]);
threads[i] = new Thread(() -> {
for (final int type : TYPES) {
test(cs, type);
}
});
threads[i].start();
}
for (int i = 0; i < CSs.length; i++) {
threads[i].join();
}
}
/**
* 1. Checks how many times the width/height are requested during filter()
* 2. Repeats step1, but returns some random data for each request
*/
private static void test(ColorSpace cs, int type) {
AtomicInteger srcCountW = new AtomicInteger();
AtomicInteger srcCountH = new AtomicInteger();
AtomicInteger dstCountW = new AtomicInteger();
AtomicInteger dstCountH = new AtomicInteger();
BufferedImage dstBI = new BufferedImage(SIZE, SIZE, type) {
public int getWidth() {
dstCountW.incrementAndGet();
return super.getWidth();
}
public int getHeight() {
dstCountH.incrementAndGet();
return super.getHeight();
}
};
BufferedImage srcBI = new BufferedImage(SIZE, SIZE, type) {
public int getWidth() {
srcCountW.incrementAndGet();
return super.getWidth();
}
public int getHeight() {
srcCountH.incrementAndGet();
return super.getHeight();
}
};
filter(srcBI, cs, dstBI);
if (dstCountW.get() == 0 && dstCountH.get() == 0
&& srcCountW.get() == 0 && srcCountH.get() == 0) {
// getWidth/getHeight are never called
return;
}
for (int brokenH : INTERESTING_POINTS) {
for (int brokenW : INTERESTING_POINTS) {
for (int srcW = 0; srcW <= srcCountW.get(); ++srcW) {
for (int srcH = 0; srcH <= srcCountH.get(); ++srcH) {
srcBI = makeBI(type, brokenH, brokenW, srcW, srcH);
for (int dstW = 0; dstW <= dstCountW.get(); ++dstW) {
for (int dstH = 0; dstH <= dstCountH.get(); ++dstH) {
try {
dstBI = makeBI(type, brokenH, brokenW, dstW, dstH);
filter(srcBI, cs, dstBI);
} catch (Exception | OutOfMemoryError ignore) {
}
}
}
}
}
}
}
}
private static BufferedImage makeBI(int biType, int brokenH, int brokenW,
int breakStepW, int breakStepH) {
return new BufferedImage(SIZE, SIZE, biType) {
private int stepW = 0;
private int stepH = 0;
public int getWidth() {
if (stepW == breakStepW) {
return brokenW;
}
stepW++;
return super.getWidth();
}
public int getHeight() {
if (stepH == breakStepH) {
return brokenH;
}
stepH++;
return super.getHeight();
}
};
}
private static void filter(BufferedImage src, ColorSpace to,
BufferedImage dest) {
ColorConvertOp op = new ColorConvertOp(to, null);
op.filter(src, dest);
}
}