From aed78f3c9b902d73888c2e5954aba0a9e747f685 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 8 Apr 2020 15:11:38 +0300 Subject: [PATCH] Bound commands limit and throw interpreter exception upon exceeding --- .../common/interpreter/IrInterpreter.kt | 31 ++++++++++++++----- .../exceptions/InterpreterException.kt | 9 ++++++ .../exceptions/InterpreterTimeOutException.kt | 11 +++++++ 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterException.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterTimeOutException.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index 02038a9dda9..144ff78f3bb 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.backend.common.interpreter import kotlinx.coroutines.* import org.jetbrains.kotlin.backend.common.interpreter.builtins.* +import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterException +import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterTimeOutException import org.jetbrains.kotlin.backend.common.interpreter.stack.* import org.jetbrains.kotlin.backend.common.interpreter.state.* import org.jetbrains.kotlin.builtins.DefaultBuiltIns @@ -23,6 +25,7 @@ import org.jetbrains.kotlin.ir.util.* import java.lang.invoke.MethodHandle private const val MAX_STACK_SIZE = 10_000 +private const val MAX_COMMANDS = 500_000 class IrInterpreter(irModule: IrModuleFragment) { private val irBuiltIns = irModule.irBuiltins @@ -32,6 +35,7 @@ class IrInterpreter(irModule: IrModuleFragment) { private val illegalArgumentException = irExceptions.first { it.name.asString() == IllegalArgumentException::class.java.simpleName } private val stackTrace = mutableListOf() + private var commandCount = 0 private val mapOfEnums = mutableMapOf, Complex>() @@ -51,22 +55,33 @@ class IrInterpreter(irModule: IrModuleFragment) { } } + private fun incrementAndCheckCommands() { + commandCount++ + if (commandCount >= MAX_COMMANDS) throw InterpreterTimeOutException() + } + fun interpret(expression: IrExpression): IrExpression { val data = InterpreterFrame() - return runBlocking { - return@runBlocking when (val returnLabel = withContext(this.coroutineContext) { expression.interpret(data).returnLabel }) { - ReturnLabel.NEXT -> data.popReturnValue().toIrExpression(expression) - ReturnLabel.EXCEPTION -> { - val message = (data.popReturnValue() as ExceptionState).getFullDescription() - IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message) + return try { + runBlocking { + return@runBlocking when (val returnLabel = withContext(this.coroutineContext) { expression.interpret(data).returnLabel }) { + ReturnLabel.NEXT -> data.popReturnValue().toIrExpression(expression) + ReturnLabel.EXCEPTION -> { + val message = (data.popReturnValue() as ExceptionState).getFullDescription() + IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message) + } + else -> TODO("$returnLabel not supported as result of interpretation") } - else -> TODO("$returnLabel not supported as result of interpretation") } + } catch (e: InterpreterException) { + // TODO don't handle, throw to lowering + IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + e.message) } } private suspend fun IrElement.interpret(data: Frame): ExecutionResult { try { + incrementAndCheckCommands() val executionResult = when (this) { is IrFunctionImpl -> interpretFunction(this, data) is IrCall -> interpretCall(this, data) @@ -106,6 +121,8 @@ class IrInterpreter(irModule: IrModuleFragment) { } return executionResult.getNextLabel(this, data) { this@getNextLabel.interpret(it) } + } catch (e: InterpreterException) { + throw e } catch (e: Throwable) { // catch exception from JVM such as: ArithmeticException, StackOverflowError and others val exceptionName = e::class.java.simpleName diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterException.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterException.kt new file mode 100644 index 00000000000..1cf1e848876 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterException.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.interpreter.exceptions + +abstract class InterpreterException : Exception() { +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterTimeOutException.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterTimeOutException.kt new file mode 100644 index 00000000000..f487753b190 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/exceptions/InterpreterTimeOutException.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.interpreter.exceptions + +class InterpreterTimeOutException : InterpreterException() { + override val message: String? + get() = "Exceeded execution limit of constexpr expression" +} \ No newline at end of file