Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to get StackOverflowError without recursion?

I have a task to get "StackOverflowError" in java without using -Xss and recursion. I really don't have ideas... Only some nonsense like generating huge java class at runtime, compile it and invoke...

like image 601
Вадим Парафенюк Avatar asked Mar 02 '18 20:03

Вадим Парафенюк


People also ask

Can you catch StackOverflowError Java?

StackOverflowError is an error which Java doesn't allow to catch, for instance, stack running out of space, as it's one of the most common runtime errors one can encounter.

What would cause a StackOverflowError to occur in recursion?

The most-common cause of stack overflow is excessively deep or infinite recursion, in which a function calls itself so many times that the space needed to store the variables and information associated with each call is more than can fit on the stack.

What causes StackOverflowError?

StackOverflowError is a runtime error which points to serious problems that cannot be caught by an application. The java. lang. StackOverflowError indicates that the application stack is exhausted and is usually caused by deep or infinite recursion.


2 Answers

Java stores primitive types on the stack. Objects created in local scope are allocated on the heap, with the reference to them on the stack.

You can overflow the stack without recursion by allocating too many primitive types in method scope. With normal stack size settings, you would have to allocate an excessive number of variables to overflow.

like image 121
Eric J. Avatar answered Oct 10 '22 00:10

Eric J.


Here is the implementation of Eric J. idea of generating excessive number of local variables using javassist library:

class SoeNonRecursive {
    static final String generatedMethodName = "holderForVariablesMethod";

    @SneakyThrows
    Class<?> createClassWithLotsOfLocalVars(String generatedClassName, final int numberOfLocalVarsToGenerate) {
        ClassPool pool = ClassPool.getDefault();
        CtClass generatedClass = pool.makeClass(generatedClassName);
        CtMethod generatedMethod = CtNewMethod.make(getMethodBody(numberOfLocalVarsToGenerate), generatedClass);
        generatedClass.addMethod(generatedMethod);
        return generatedClass.toClass();
    }

    private String getMethodBody(final int numberOfLocalVarsToGenerate) {
        StringBuilder methodBody = new StringBuilder("public static long ")
                .append(generatedMethodName).append("() {")
                .append(System.lineSeparator());
        StringBuilder antiDeadCodeEliminationString = new StringBuilder("long result = i0");
        long i = 0;
        while (i < numberOfLocalVarsToGenerate) {
            methodBody.append("  long i").append(i)
                      .append(" = ").append(i).append(";")
                      .append(System.lineSeparator());
            antiDeadCodeEliminationString.append("+").append("i").append(i);
            i++;
        }
        antiDeadCodeEliminationString.append(";");
        methodBody.append("  ").append(antiDeadCodeEliminationString)
                  .append(System.lineSeparator())
                  .append("  return result;")
                  .append(System.lineSeparator())
                  .append("}");
        return methodBody.toString();
    }
}

and tests:

class SoeNonRecursiveTest {
    private final SoeNonRecursive soeNonRecursive = new SoeNonRecursive();
    //Should be different for every case, or once generated class become
    //"frozen" for javassist: http://www.javassist.org/tutorial/tutorial.html#read
    private String generatedClassName;

    @Test
    void stackOverflowWithoutRecursion() {
        generatedClassName = "Soe1";
        final int numberOfLocalVarsToGenerate = 6000;
        assertThrows(StackOverflowError.class, () -> soeNonRecursive
                .createClassWithLotsOfLocalVars(generatedClassName, numberOfLocalVarsToGenerate));
    }

    @SneakyThrows
    @Test
    void methodGeneratedCorrectly() {
        generatedClassName = "Soe2";
        final int numberOfLocalVarsToGenerate = 6;
        Class<?> generated = soeNonRecursive.createClassWithLotsOfLocalVars(generatedClassName, numberOfLocalVarsToGenerate);
        //Arithmetic progression
        long expected = Math.round((numberOfLocalVarsToGenerate - 1.0)/2 * numberOfLocalVarsToGenerate);
        long actual = (long) generated.getDeclaredMethod(generatedMethodName).invoke(generated);
        assertEquals(expected, actual);
    }
}
like image 21
Alexander Radchenko Avatar answered Oct 10 '22 01:10

Alexander Radchenko