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
431
test/jdk/java/foreign/TestMismatch.java
Normal file
431
test/jdk/java/foreign/TestMismatch.java
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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
|
||||
* @bug 8323552
|
||||
* @run testng/timeout=480 TestMismatch
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
public class TestMismatch {
|
||||
|
||||
// stores an increasing sequence of values into the memory of the given segment
|
||||
static MemorySegment initializeSegment(MemorySegment segment) {
|
||||
for (int i = 0 ; i < segment.byteSize() ; i++) {
|
||||
segment.set(ValueLayout.JAVA_BYTE, i, (byte)i);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeSrcFromOffset(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, -1, 0, s2, 0, 0);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeDstFromOffset(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, 0, 0, s2, -1, 0);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeSrcToOffset(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, 0, -1, s2, 0, 0);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeDstToOffset(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, 0, 0, s2, 0, -1);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeSrcLength(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, 3, 2, s2, 0, 0);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices", expectedExceptions = IndexOutOfBoundsException.class)
|
||||
public void testNegativeDstLength(MemorySegment s1, MemorySegment s2) {
|
||||
MemorySegment.mismatch(s1, 0, 0, s2, 3, 2);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testSameValues(MemorySegment ss1, MemorySegment ss2) {
|
||||
out.format("testSameValues s1:%s, s2:%s\n", ss1, ss2);
|
||||
MemorySegment s1 = initializeSegment(ss1);
|
||||
MemorySegment s2 = initializeSegment(ss2);
|
||||
|
||||
if (s1.byteSize() == s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), -1); // identical
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
} else if (s1.byteSize() > s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), s2.byteSize()); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), s2.byteSize());
|
||||
} else {
|
||||
assert s1.byteSize() < s2.byteSize();
|
||||
assertEquals(s1.mismatch(s2), s1.byteSize()); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), s1.byteSize());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slicesStatic")
|
||||
public void testSameValuesStatic(SliceOffsetAndSize ss1, SliceOffsetAndSize ss2) {
|
||||
out.format("testSameValuesStatic s1:%s, s2:%s\n", ss1, ss2);
|
||||
MemorySegment s1 = initializeSegment(ss1.toSlice());
|
||||
MemorySegment s2 = initializeSegment(ss2.toSlice());
|
||||
|
||||
for (long i = ss2.offset ; i < ss2.size ; i++) {
|
||||
long bytes = i - ss2.offset;
|
||||
long expected = (bytes == ss1.size) ?
|
||||
-1 : Long.min(ss1.size, bytes);
|
||||
assertEquals(MemorySegment.mismatch(ss1.segment, ss1.offset, ss1.endOffset(), ss2.segment, ss2.offset, i), expected);
|
||||
}
|
||||
for (long i = ss1.offset ; i < ss1.size ; i++) {
|
||||
long bytes = i - ss1.offset;
|
||||
long expected = (bytes == ss2.size) ?
|
||||
-1 : Long.min(ss2.size, bytes);
|
||||
assertEquals(MemorySegment.mismatch(ss2.segment, ss2.offset, ss2.endOffset(), ss1.segment, ss1.offset, i), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void random() {
|
||||
try (var arena = Arena.ofConfined()) {
|
||||
var rnd = new Random(42);
|
||||
for (int size = 1; size < 64; size++) {
|
||||
// Repeat a fair number of rounds
|
||||
for (int i = 0; i < 147; i++) {
|
||||
var src = arena.allocate(size);
|
||||
// The dst segment might be zero to eight bytes longer
|
||||
var dst = arena.allocate(size + rnd.nextInt(8 + 1));
|
||||
// Fill the src with random data
|
||||
for (int j = 0; j < size; j++) {
|
||||
src.set(ValueLayout.JAVA_BYTE, j, randomByte(rnd));
|
||||
}
|
||||
// copy the random data from src to dst
|
||||
dst.copyFrom(src);
|
||||
// Fill the rest (if any) of the dst with random data
|
||||
for (long j = src.byteSize(); j < dst.byteSize(); j++) {
|
||||
dst.set(ValueLayout.JAVA_BYTE, j, randomByte(rnd));
|
||||
}
|
||||
|
||||
if (rnd.nextBoolean()) {
|
||||
// In this branch, we inject one or more deviating bytes
|
||||
int beginDiff = rnd.nextInt(size);
|
||||
int endDiff = rnd.nextInt(beginDiff, size);
|
||||
for (int d = beginDiff; d <= endDiff; d++) {
|
||||
byte existing = dst.get(ValueLayout.JAVA_BYTE, d);
|
||||
// Make sure we never get back the same value
|
||||
byte mutatedValue;
|
||||
do {
|
||||
mutatedValue = randomByte(rnd);
|
||||
} while (existing == mutatedValue);
|
||||
dst.set(ValueLayout.JAVA_BYTE, d, mutatedValue);
|
||||
}
|
||||
|
||||
// They are not equal and differs in position beginDiff
|
||||
assertEquals(src.mismatch(dst), beginDiff);
|
||||
assertEquals(dst.mismatch(src), beginDiff);
|
||||
} else {
|
||||
// In this branch, there is no injection
|
||||
|
||||
if (src.byteSize() == dst.byteSize()) {
|
||||
// The content matches and they are of equal size
|
||||
assertEquals(src.mismatch(dst), -1);
|
||||
assertEquals(dst.mismatch(src), -1);
|
||||
} else {
|
||||
// The content matches but they are of different length
|
||||
// Remember, the size of src is always smaller or equal
|
||||
// to the size of dst.
|
||||
assertEquals(src.mismatch(dst), src.byteSize());
|
||||
assertEquals(dst.mismatch(src), src.byteSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static byte randomByte(Random rnd) {
|
||||
return (byte) rnd.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE + 1);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testDifferentValues(MemorySegment s1, MemorySegment s2) {
|
||||
out.format("testDifferentValues s1:%s, s2:%s\n", s1, s2);
|
||||
s1 = initializeSegment(s1);
|
||||
s2 = initializeSegment(s2);
|
||||
|
||||
for (long i = s2.byteSize() -1 ; i >= 0; i--) {
|
||||
long expectedMismatchOffset = i;
|
||||
s2.set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF);
|
||||
|
||||
if (s1.byteSize() == s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
} else if (s1.byteSize() > s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
} else {
|
||||
assert s1.byteSize() < s2.byteSize();
|
||||
var off = Math.min(s1.byteSize(), expectedMismatchOffset);
|
||||
assertEquals(s1.mismatch(s2), off); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slicesStatic")
|
||||
public void testDifferentValuesStatic(SliceOffsetAndSize ss1, SliceOffsetAndSize ss2) {
|
||||
out.format("testDifferentValues s1:%s, s2:%s\n", ss1, ss2);
|
||||
|
||||
for (long i = ss2.size - 1 ; i >= 0; i--) {
|
||||
if (i >= ss1.size) continue;
|
||||
initializeSegment(ss1.toSlice());
|
||||
initializeSegment(ss2.toSlice());
|
||||
long expectedMismatchOffset = i;
|
||||
ss2.toSlice().set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF);
|
||||
|
||||
for (long j = expectedMismatchOffset + 1 ; j < ss2.size ; j++) {
|
||||
assertEquals(MemorySegment.mismatch(ss1.segment, ss1.offset, ss1.endOffset(), ss2.segment, ss2.offset, j + ss2.offset), expectedMismatchOffset);
|
||||
}
|
||||
for (long j = expectedMismatchOffset + 1 ; j < ss1.size ; j++) {
|
||||
assertEquals(MemorySegment.mismatch(ss2.segment, ss2.offset, ss2.endOffset(), ss1.segment, ss1.offset, j + ss1.offset), expectedMismatchOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
var s1 = MemorySegment.ofArray(new byte[0]);
|
||||
assertEquals(s1.mismatch(s1), -1);
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
var nativeSegment = arena.allocate(4, 4);;
|
||||
var s2 = nativeSegment.asSlice(0, 0);
|
||||
assertEquals(s1.mismatch(s2), -1);
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLarge() {
|
||||
// skip if not on 64 bits
|
||||
if (ValueLayout.ADDRESS.byteSize() > 32) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
var s1 = arena.allocate((long) Integer.MAX_VALUE + 10L, 8);;
|
||||
var s2 = arena.allocate((long) Integer.MAX_VALUE + 10L, 8);;
|
||||
assertEquals(s1.mismatch(s1), -1);
|
||||
assertEquals(s1.mismatch(s2), -1);
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
|
||||
testLargeAcrossMaxBoundary(s1, s2);
|
||||
|
||||
testLargeMismatchAcrossMaxBoundary(s1, s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testLargeAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) {
|
||||
for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
|
||||
var s3 = s1.asSlice(0, i);
|
||||
var s4 = s2.asSlice(0, i);
|
||||
// instance
|
||||
assertEquals(s3.mismatch(s3), -1);
|
||||
assertEquals(s3.mismatch(s4), -1);
|
||||
assertEquals(s4.mismatch(s3), -1);
|
||||
// static
|
||||
assertEquals(MemorySegment.mismatch(s1, 0, s1.byteSize(), s1, 0, i), -1);
|
||||
assertEquals(MemorySegment.mismatch(s2, 0, s1.byteSize(), s1, 0, i), -1);
|
||||
assertEquals(MemorySegment.mismatch(s1, 0, s1.byteSize(), s2, 0, i), -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) {
|
||||
for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
|
||||
s2.set(ValueLayout.JAVA_BYTE, i, (byte) 0xFF);
|
||||
long expectedMismatchOffset = i;
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
}
|
||||
}
|
||||
|
||||
static final Class<IllegalStateException> ISE = IllegalStateException.class;
|
||||
static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
|
||||
|
||||
@Test
|
||||
public void testClosed() {
|
||||
MemorySegment s1, s2;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
s1 = arena.allocate(4, 1);
|
||||
s2 = arena.allocate(4, 1);;
|
||||
}
|
||||
assertThrows(ISE, () -> s1.mismatch(s1));
|
||||
assertThrows(ISE, () -> s1.mismatch(s2));
|
||||
assertThrows(ISE, () -> s2.mismatch(s1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreadAccess() throws Exception {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
var segment = arena.allocate(4, 1);;
|
||||
{
|
||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
MemorySegment.ofArray(new byte[4]).mismatch(segment);
|
||||
} catch (RuntimeException e) {
|
||||
exception.set(e);
|
||||
}
|
||||
};
|
||||
Thread thread = new Thread(action);
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
RuntimeException e = exception.get();
|
||||
if (!(e instanceof WrongThreadException)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
{
|
||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
segment.mismatch(MemorySegment.ofArray(new byte[4]));
|
||||
} catch (RuntimeException e) {
|
||||
exception.set(e);
|
||||
}
|
||||
};
|
||||
Thread thread = new Thread(action);
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
RuntimeException e = exception.get();
|
||||
if (!(e instanceof WrongThreadException)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameSegment() {
|
||||
var segment = MemorySegment.ofArray(new byte[]{
|
||||
1,2,3,4, 1,2,3,4, 1,4});
|
||||
|
||||
long match = MemorySegment.mismatch(
|
||||
segment, 0L, 4L,
|
||||
segment, 4L, 8L);
|
||||
assertEquals(match, -1);
|
||||
|
||||
long noMatch = MemorySegment.mismatch(
|
||||
segment, 0L, 4L,
|
||||
segment, 1L, 5L);
|
||||
assertEquals(noMatch, 0);
|
||||
|
||||
long noMatchEnd = MemorySegment.mismatch(
|
||||
segment, 0L, 2L,
|
||||
segment, 8L, 10L);
|
||||
assertEquals(noMatchEnd, 1);
|
||||
|
||||
long same = MemorySegment.mismatch(
|
||||
segment, 0L, 8L,
|
||||
segment, 0L, 8L);
|
||||
assertEquals(same, -1);
|
||||
}
|
||||
|
||||
enum SegmentKind {
|
||||
NATIVE(i -> Arena.ofAuto().allocate(i, 1)),
|
||||
ARRAY(i -> MemorySegment.ofArray(new byte[i]));
|
||||
|
||||
final IntFunction<MemorySegment> segmentFactory;
|
||||
|
||||
SegmentKind(IntFunction<MemorySegment> segmentFactory) {
|
||||
this.segmentFactory = segmentFactory;
|
||||
}
|
||||
|
||||
MemorySegment makeSegment(int elems) {
|
||||
return segmentFactory.apply(elems);
|
||||
}
|
||||
}
|
||||
|
||||
record SliceOffsetAndSize(MemorySegment segment, long offset, long size) {
|
||||
MemorySegment toSlice() {
|
||||
return segment.asSlice(offset, size);
|
||||
}
|
||||
long endOffset() {
|
||||
return offset + size;
|
||||
}
|
||||
};
|
||||
|
||||
@DataProvider(name = "slicesStatic")
|
||||
static Object[][] slicesStatic() {
|
||||
int[] sizes = { 16, 8, 1 };
|
||||
List<SliceOffsetAndSize> aSliceOffsetAndSizes = new ArrayList<>();
|
||||
List<SliceOffsetAndSize> bSliceOffsetAndSizes = new ArrayList<>();
|
||||
for (List<SliceOffsetAndSize> slices : List.of(aSliceOffsetAndSizes, bSliceOffsetAndSizes)) {
|
||||
for (SegmentKind kind : SegmentKind.values()) {
|
||||
MemorySegment segment = kind.makeSegment(16);
|
||||
//compute all slices
|
||||
for (int size : sizes) {
|
||||
for (int index = 0 ; index < 16 ; index += size) {
|
||||
slices.add(new SliceOffsetAndSize(segment, index, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert aSliceOffsetAndSizes.size() == bSliceOffsetAndSizes.size();
|
||||
Object[][] sliceArray = new Object[aSliceOffsetAndSizes.size() * bSliceOffsetAndSizes.size()][];
|
||||
for (int i = 0 ; i < aSliceOffsetAndSizes.size() ; i++) {
|
||||
for (int j = 0 ; j < bSliceOffsetAndSizes.size() ; j++) {
|
||||
sliceArray[i * aSliceOffsetAndSizes.size() + j] = new Object[] { aSliceOffsetAndSizes.get(i), bSliceOffsetAndSizes.get(j) };
|
||||
}
|
||||
}
|
||||
return sliceArray;
|
||||
}
|
||||
|
||||
@DataProvider(name = "slices")
|
||||
static Object[][] slices() {
|
||||
Object[][] slicesStatic = slicesStatic();
|
||||
return Stream.of(slicesStatic)
|
||||
.map(arr -> new Object[]{
|
||||
((SliceOffsetAndSize) arr[0]).toSlice(),
|
||||
((SliceOffsetAndSize) arr[1]).toSlice()
|
||||
}).toArray(Object[][]::new);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue