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
262
test/jdk/java/lang/ScopedValue/StressStackOverflow.java
Normal file
262
test/jdk/java/lang/ScopedValue/StressStackOverflow.java
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022 Red Hat, Inc. 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 id=default
|
||||
* @summary Stress ScopedValue stack overflow recovery path
|
||||
* @enablePreview
|
||||
* @run main/othervm/timeout=300 StressStackOverflow
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=no-TieredCompilation
|
||||
* @enablePreview
|
||||
* @run main/othervm/timeout=300 -XX:-TieredCompilation StressStackOverflow
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=TieredStopAtLevel1
|
||||
* @enablePreview
|
||||
* @run main/othervm/timeout=300 -XX:TieredStopAtLevel=1 StressStackOverflow
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=no-vmcontinuations
|
||||
* @requires vm.continuations
|
||||
* @enablePreview
|
||||
* @run main/othervm/timeout=300 -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations StressStackOverflow
|
||||
*/
|
||||
|
||||
import java.lang.ScopedValue.CallableOp;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.StructureViolationException;
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
import java.util.concurrent.StructuredTaskScope.Joiner;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class StressStackOverflow {
|
||||
public static final ScopedValue<Integer> el = ScopedValue.newInstance();
|
||||
|
||||
public static final ScopedValue<Integer> inheritedValue = ScopedValue.newInstance();
|
||||
|
||||
static final TestFailureException testFailureException = new TestFailureException("Unexpected value for ScopedValue");
|
||||
int ITERS = 1_000_000;
|
||||
|
||||
static class TestFailureException extends RuntimeException {
|
||||
TestFailureException(String s) { super(s); }
|
||||
}
|
||||
|
||||
static final long DURATION_IN_NANOS = Duration.ofMinutes(1).toNanos();
|
||||
|
||||
// Test the ScopedValue recovery mechanism for stack overflows. We implement both CallableOp
|
||||
// and Runnable interfaces. Which one gets tested depends on the constructor argument.
|
||||
class DeepRecursion implements CallableOp<Object, RuntimeException>, Supplier<Object>, Runnable {
|
||||
|
||||
enum Behaviour {
|
||||
CALL, RUN;
|
||||
private static final Behaviour[] values = values();
|
||||
public static Behaviour choose(ThreadLocalRandom tlr) {
|
||||
return values[tlr.nextInt(3)];
|
||||
}
|
||||
}
|
||||
|
||||
final Behaviour behaviour;
|
||||
|
||||
public DeepRecursion(Behaviour behaviour) {
|
||||
this.behaviour = behaviour;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final var last = el.get();
|
||||
while (ITERS-- > 0) {
|
||||
if (System.nanoTime() - startTime > DURATION_IN_NANOS) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nextRandomFloat = ThreadLocalRandom.current().nextFloat();
|
||||
try {
|
||||
switch (behaviour) {
|
||||
case CALL -> ScopedValue.where(el, el.get() + 1).call(() -> fibonacci_pad(20, this));
|
||||
case RUN -> ScopedValue.where(el, el.get() + 1).run(() -> fibonacci_pad(20, this));
|
||||
}
|
||||
if (!last.equals(el.get())) {
|
||||
throw testFailureException;
|
||||
}
|
||||
} catch (StackOverflowError e) {
|
||||
if (nextRandomFloat <= 0.1) {
|
||||
ScopedValue.where(el, el.get() + 1).run(this);
|
||||
}
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Throwable throwable) {
|
||||
// StackOverflowErrors cause many different failures. These include
|
||||
// StructureViolationExceptions and InvocationTargetExceptions. This test
|
||||
// checks that, no matter what the failure mode, scoped values are handled
|
||||
// correctly.
|
||||
} finally {
|
||||
if (!last.equals(el.get())) {
|
||||
throw testFailureException;
|
||||
}
|
||||
}
|
||||
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
public Object get() {
|
||||
run();
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object call() {
|
||||
return get();
|
||||
}
|
||||
}
|
||||
|
||||
static final Runnable nop = () -> {};
|
||||
|
||||
// Consume some stack.
|
||||
//
|
||||
// The double recursion used here prevents an optimizing JIT from
|
||||
// inlining all the recursive calls, which would make it
|
||||
// ineffective.
|
||||
private long fibonacci_pad1(int n, Runnable op) {
|
||||
if (n <= 1) {
|
||||
op.run();
|
||||
return n;
|
||||
}
|
||||
return fibonacci_pad1(n - 1, op) + fibonacci_pad1(n - 2, nop);
|
||||
}
|
||||
|
||||
private static final Integer I_42 = 42;
|
||||
|
||||
long fibonacci_pad(int n, Runnable op) {
|
||||
final var last = el.get();
|
||||
try {
|
||||
return fibonacci_pad1(ThreadLocalRandom.current().nextInt(n), op);
|
||||
} catch (StackOverflowError err) {
|
||||
if (!inheritedValue.get().equals(I_42)) {
|
||||
throw testFailureException;
|
||||
}
|
||||
if (!last.equals(el.get())) {
|
||||
throw testFailureException;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Run op in a new thread. Platform or virtual threads are chosen at random.
|
||||
void runInNewThread(Runnable op) {
|
||||
var threadFactory
|
||||
= (ThreadLocalRandom.current().nextBoolean() ? Thread.ofPlatform() : Thread.ofVirtual()).factory();
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withThreadFactory(threadFactory))) {
|
||||
var handle = scope.fork(() -> {
|
||||
op.run();
|
||||
return null;
|
||||
});
|
||||
scope.join();
|
||||
handle.get();
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
ScopedValue.where(inheritedValue, 42).where(el, 0).run(() -> {
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
|
||||
try {
|
||||
if (ThreadLocalRandom.current().nextBoolean()) {
|
||||
// Repeatedly test Scoped Values set by ScopedValue::call(), get(), and run()
|
||||
final var deepRecursion
|
||||
= new DeepRecursion(DeepRecursion.Behaviour.choose(ThreadLocalRandom.current()));
|
||||
deepRecursion.run();
|
||||
} else {
|
||||
// Recursively run ourself until we get a stack overflow
|
||||
// Catch the overflow and make sure the recovery path works
|
||||
// for values inherited from a StructuredTaskScope.
|
||||
Runnable op = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
fibonacci_pad(20, this);
|
||||
} catch (StackOverflowError e) {
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Throwable throwable) {
|
||||
// StackOverflowErrors cause many different failures. These include
|
||||
// StructureViolationExceptions and InvocationTargetExceptions. This test
|
||||
// checks that, no matter what the failure mode, scoped values are handled
|
||||
// correctly.
|
||||
} finally {
|
||||
if (!inheritedValue.get().equals(I_42)) {
|
||||
throw testFailureException;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
runInNewThread(op);
|
||||
}
|
||||
scope.join();
|
||||
} catch (StructureViolationException structureViolationException) {
|
||||
// Can happen if a stack overflow prevented a StackableScope from
|
||||
// being removed. We can continue.
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Can happen if a stack overflow prevented a StackableScope from
|
||||
// being removed. We can continue.
|
||||
}
|
||||
}
|
||||
|
||||
static long startTime = System.nanoTime();
|
||||
|
||||
public static void main(String[] args) {
|
||||
var torture = new StressStackOverflow();
|
||||
while (torture.ITERS > 0
|
||||
&& System.nanoTime() - startTime <= DURATION_IN_NANOS) {
|
||||
try {
|
||||
torture.run();
|
||||
if (inheritedValue.isBound()) {
|
||||
throw new TestFailureException("Should not be bound here");
|
||||
}
|
||||
} catch (TestFailureException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// ScopedValueContainer and StructuredTaskScope can
|
||||
// throw many exceptions on stack overflow. Ignore
|
||||
// them all.
|
||||
}
|
||||
}
|
||||
System.out.println("OK");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue