undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
201
test/jdk/java/foreign/normalize/TestNormalize.java
Normal file
201
test/jdk/java/foreign/normalize/TestNormalize.java
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @library ../
|
||||
* @run testng/othervm/native
|
||||
* --enable-native-access=ALL-UNNAMED
|
||||
* -Xbatch
|
||||
* -XX:CompileCommand=dontinline,TestNormalize::doCall*
|
||||
* TestNormalize
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_CHAR;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
// test normalization of smaller than int primitive types
|
||||
public class TestNormalize extends NativeTestHelper {
|
||||
|
||||
private static final Linker LINKER = Linker.nativeLinker();
|
||||
private static final MethodHandle SAVE_BOOLEAN_AS_INT;
|
||||
private static final MethodHandle SAVE_BYTE_AS_INT;
|
||||
private static final MethodHandle SAVE_SHORT_AS_INT;
|
||||
private static final MethodHandle SAVE_CHAR_AS_INT;
|
||||
|
||||
private static final MethodHandle BOOLEAN_TO_INT;
|
||||
private static final MethodHandle BYTE_TO_INT;
|
||||
private static final MethodHandle SHORT_TO_INT;
|
||||
private static final MethodHandle CHAR_TO_INT;
|
||||
|
||||
private static final MethodHandle NATIVE_BOOLEAN_TO_INT;
|
||||
|
||||
private static final int BOOLEAN_HOB_MASK = ~0b1;
|
||||
private static final int BYTE_HOB_MASK = ~0xFF;
|
||||
private static final int SHORT_HOB_MASK = ~0xFFFF;
|
||||
private static final int CHAR_HOB_MASK = ~0xFFFF;
|
||||
|
||||
private static final MethodHandle SAVE_BOOLEAN;
|
||||
|
||||
static {
|
||||
System.loadLibrary("Normalize");
|
||||
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
SAVE_BOOLEAN_AS_INT = lookup.findStatic(TestNormalize.class, "saveBooleanAsInt", MethodType.methodType(void.class, boolean.class, int[].class));
|
||||
SAVE_BYTE_AS_INT = lookup.findStatic(TestNormalize.class, "saveByteAsInt", MethodType.methodType(void.class, byte.class, int[].class));
|
||||
SAVE_SHORT_AS_INT = lookup.findStatic(TestNormalize.class, "saveShortAsInt", MethodType.methodType(void.class, short.class, int[].class));
|
||||
SAVE_CHAR_AS_INT = lookup.findStatic(TestNormalize.class, "saveCharAsInt", MethodType.methodType(void.class, char.class, int[].class));
|
||||
|
||||
BOOLEAN_TO_INT = lookup.findStatic(TestNormalize.class, "booleanToInt", MethodType.methodType(int.class, boolean.class));
|
||||
BYTE_TO_INT = lookup.findStatic(TestNormalize.class, "byteToInt", MethodType.methodType(int.class, byte.class));
|
||||
SHORT_TO_INT = lookup.findStatic(TestNormalize.class, "shortToInt", MethodType.methodType(int.class, short.class));
|
||||
CHAR_TO_INT = lookup.findStatic(TestNormalize.class, "charToInt", MethodType.methodType(int.class, char.class));
|
||||
|
||||
NATIVE_BOOLEAN_TO_INT = LINKER.downcallHandle(findNativeOrThrow("int_identity"), FunctionDescriptor.of(JAVA_INT, JAVA_BOOLEAN));
|
||||
|
||||
SAVE_BOOLEAN = lookup.findStatic(TestNormalize.class, "saveBoolean", MethodType.methodType(void.class, boolean.class, boolean[].class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// The idea of this test is that we pass a 'dirty' int value down to native code, and then receive it back
|
||||
// as the argument to an upcall, as well as the result of the downcall, but with a sub-int type (boolean, byte, short, char).
|
||||
// When we do either of those, argument normalization should take place, so that the resulting value is sane (1).
|
||||
// After that we convert the value back to int again, the JVM can/will skip value normalization here.
|
||||
// We then check the high order bits of the resulting int. If argument normalization took place at (1), they should all be 0.
|
||||
@Test(dataProvider = "cases")
|
||||
public void testNormalize(ValueLayout layout, int testValue, int hobMask, MethodHandle toInt, MethodHandle saver) throws Throwable {
|
||||
// use actual type as parameter type to test upcall arg normalization
|
||||
FunctionDescriptor upcallDesc = FunctionDescriptor.ofVoid(layout);
|
||||
// use actual type as return type to test downcall return normalization
|
||||
FunctionDescriptor downcallDesc = FunctionDescriptor.of(layout, ADDRESS, JAVA_INT);
|
||||
|
||||
MemorySegment target = findNativeOrThrow("test");
|
||||
MethodHandle downcallHandle = LINKER.downcallHandle(target, downcallDesc);
|
||||
downcallHandle = MethodHandles.filterReturnValue(downcallHandle, toInt);
|
||||
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
int[] box = new int[1];
|
||||
saver = MethodHandles.insertArguments(saver, 1, box);
|
||||
MemorySegment upcallStub = LINKER.upcallStub(saver, upcallDesc, arena);
|
||||
int dirtyValue = testValue | hobMask; // set all bits that should not be set
|
||||
|
||||
// test after JIT as well
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
doCall(downcallHandle, upcallStub, box, dirtyValue, hobMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void doCall(MethodHandle downcallHandle, MemorySegment upcallStub,
|
||||
int[] box, int dirtyValue, int hobMask) throws Throwable {
|
||||
int result = (int) downcallHandle.invokeExact(upcallStub, dirtyValue);
|
||||
assertEquals(box[0] & hobMask, 0); // check normalized upcall arg
|
||||
assertEquals(result & hobMask, 0); // check normalized downcall return value
|
||||
}
|
||||
|
||||
public static void saveBooleanAsInt(boolean b, int[] box) {
|
||||
box[0] = booleanToInt(b);
|
||||
}
|
||||
public static void saveByteAsInt(byte b, int[] box) {
|
||||
box[0] = byteToInt(b);
|
||||
}
|
||||
public static void saveShortAsInt(short s, int[] box) {
|
||||
box[0] = shortToInt(s);
|
||||
}
|
||||
public static void saveCharAsInt(char c, int[] box) {
|
||||
box[0] = charToInt(c);
|
||||
}
|
||||
|
||||
public static int booleanToInt(boolean b) {
|
||||
try {
|
||||
return (int) NATIVE_BOOLEAN_TO_INT.invokeExact(b); // FIXME do in pure Java?
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public static int byteToInt(byte b) {
|
||||
return b;
|
||||
}
|
||||
public static int charToInt(char c) {
|
||||
return c;
|
||||
}
|
||||
public static int shortToInt(short s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] cases() {
|
||||
return new Object[][] {
|
||||
{ JAVA_BOOLEAN, booleanToInt(true), BOOLEAN_HOB_MASK, BOOLEAN_TO_INT, SAVE_BOOLEAN_AS_INT },
|
||||
{ JAVA_BYTE, byteToInt((byte) 42), BYTE_HOB_MASK, BYTE_TO_INT, SAVE_BYTE_AS_INT },
|
||||
{ JAVA_SHORT, shortToInt((short) 42), SHORT_HOB_MASK, SHORT_TO_INT, SAVE_SHORT_AS_INT },
|
||||
{ JAVA_CHAR, charToInt('a'), CHAR_HOB_MASK, CHAR_TO_INT, SAVE_CHAR_AS_INT }
|
||||
};
|
||||
}
|
||||
|
||||
// test which int values are considered true and false
|
||||
// we currently convert any int with a non-zero first byte to true, otherwise false.
|
||||
@Test(dataProvider = "bools")
|
||||
public void testBool(int testValue, boolean expected) throws Throwable {
|
||||
MemorySegment addr = findNativeOrThrow("test");
|
||||
MethodHandle target = LINKER.downcallHandle(addr, FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, JAVA_INT));
|
||||
|
||||
boolean[] box = new boolean[1];
|
||||
MethodHandle upcallTarget = MethodHandles.insertArguments(SAVE_BOOLEAN, 1, box);
|
||||
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment callback = LINKER.upcallStub(upcallTarget, FunctionDescriptor.ofVoid(JAVA_BOOLEAN), arena);
|
||||
boolean result = (boolean) target.invokeExact(callback, testValue);
|
||||
assertEquals(box[0], expected);
|
||||
assertEquals(result, expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveBoolean(boolean b, boolean[] box) {
|
||||
box[0] = b;
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] bools() {
|
||||
return new Object[][]{
|
||||
{ 0b10, true }, // zero least significant bit, but non-zero first byte
|
||||
{ 0b1_0000_0000, false } // zero first byte
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue