From 99fad061662239f96be8ff4ead649d115db9ec79 Mon Sep 17 00:00:00 2001 From: Dmitrii Timofeev Date: Thu, 25 Aug 2022 11:15:46 +0300 Subject: [PATCH] Fixes for analysis of reflection and Unsafe * Refactored the list of fields that can't be accessed via reflection to use the single rules for all cases (engine, concrete executor, codegen) * Updated this list for Java 11 classes * Added a check to avoid marking reflection-inaccessible static fields as meaningful (they can't be reflexively created by the codegen anyway) * Added `jdk.internal` to the list of system packages to avoid mocking, disabled mocking for classes that can't be accessed via reflection * Added a wrapper for `SecurityManager` and override for two related methods in [Class] * Added UtSettings option to disable sandbox * Temporarily disabled `StringExamplesTest.testByteToString` as flaky --- .../kotlin/org/utbot/common/ReflectionUtil.kt | 2 +- .../kotlin/org/utbot/framework/UtSettings.kt | 5 +++ .../org/utbot/framework/plugin/api/Api.kt | 2 +- .../org/utbot/engine/overrides/Class.java | 10 +++++ .../overrides/security/UtSecurityManager.java | 16 ++++++++ .../kotlin/org/utbot/engine/MockStrategy.kt | 1 + .../src/main/kotlin/org/utbot/engine/Mocks.kt | 2 + .../kotlin/org/utbot/engine/ObjectWrappers.kt | 6 +++ .../utbot/engine/SecurityManagerWrapper.kt | 39 ++++++++++++++++++ .../main/kotlin/org/utbot/engine/Traverser.kt | 5 ++- .../constructor/tree/CgMethodConstructor.kt | 4 +- .../codegen/model/util/FieldIdUtil.kt | 8 ---- .../concrete/UtExecutionInstrumentation.kt | 10 +---- .../framework/concrete/UtModelConstructor.kt | 3 ++ .../utbot/framework/util/ReflectionUtils.kt | 30 ++++++++++++++ .../utbot/examples/UtValueTestCaseChecker.kt | 13 ++++++ .../exceptions/JvmCrashExamplesTest.kt | 19 ++++++++- .../examples/strings/StringExamplesTest.kt | 1 + .../examples/unsafe/UnsafeOperationsTest.kt | 41 +++++++++++++++++++ .../instrumentation/process/ChildProcess.kt | 17 +++++--- .../process/ChildProcessRunner.kt | 9 +++- .../examples/exceptions/JvmCrashExamples.java | 25 ++++++++++- .../examples/unsafe/UnsafeOperations.java | 20 +++++++++ 23 files changed, 257 insertions(+), 31 deletions(-) create mode 100644 utbot-framework/src/main/java/org/utbot/engine/overrides/security/UtSecurityManager.java create mode 100644 utbot-framework/src/main/kotlin/org/utbot/engine/SecurityManagerWrapper.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/util/ReflectionUtils.kt create mode 100644 utbot-framework/src/test/kotlin/org/utbot/examples/unsafe/UnsafeOperationsTest.kt create mode 100644 utbot-sample/src/main/java/org/utbot/examples/unsafe/UnsafeOperations.java diff --git a/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt index 13ab683dca..5b57723a46 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/ReflectionUtil.kt @@ -72,4 +72,4 @@ inline fun Field.withAccessibility(block: () -> R): R { isAccessible = prevAccessibility setModifiers(this, prevModifiers) } -} \ No newline at end of file +} diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index f79ee02149..c4f055305e 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -406,6 +406,11 @@ object UtSettings { */ var ignoreStaticsFromTrustedLibraries by getBooleanProperty(true) + /** + * Disable sandbox in the concrete executor. All unsafe/dangerous calls will be permitted. + */ + var disableSandbox by getBooleanProperty(false) + override fun toString(): String = settingsValues .mapKeys { it.key.name } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index f069490ddf..afb56ebb20 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -922,7 +922,7 @@ open class FieldId(val declaringClass: ClassId, val name: String) { return result } - override fun toString() = safeJField.toString() + override fun toString() = safeJField?.toString() ?: "[unresolved] $declaringClass.$name" } inline fun withReflection(block: () -> T): T { diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/Class.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/Class.java index 5f472feb60..53c3b0a797 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/Class.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/Class.java @@ -7,4 +7,14 @@ public class Class { public static boolean desiredAssertionStatus() { return true; } + + private void checkMemberAccess(SecurityManager sm, int which, + java.lang.Class caller, boolean checkProxyInterfaces) { + // Do nothing to allow everything + } + + private void checkPackageAccess(SecurityManager sm, final ClassLoader ccl, + boolean checkProxyInterfaces) { + // Do nothing to allow everything + } } diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/security/UtSecurityManager.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/security/UtSecurityManager.java new file mode 100644 index 0000000000..67c635945a --- /dev/null +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/security/UtSecurityManager.java @@ -0,0 +1,16 @@ +package org.utbot.engine.overrides.security; + +import java.security.Permission; + +/** + * Overridden implementation for [java.lang.SecurityManager] class + */ +public class UtSecurityManager { + public void checkPermission(Permission perm) { + // Do nothing to allow everything + } + + public void checkPackageAccess(String pkg) { + // Do nothing to allow everything + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/MockStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/MockStrategy.kt index f4948b0587..6f2bee6b0c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/MockStrategy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/MockStrategy.kt @@ -45,6 +45,7 @@ private val systemPackages = setOf( "sun.reflect", // we cannot mock Reflection since mockers are using it during the execution "java.awt", "sun.misc", + "jdk.internal", "kotlin.jvm.internal", "kotlin.internal" ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt index 7fd0f2252e..83690318cf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -19,6 +19,7 @@ import kotlin.reflect.KFunction2 import kotlin.reflect.KFunction5 import kotlinx.collections.immutable.persistentListOf import org.utbot.engine.util.mockListeners.MockListenerController +import org.utbot.framework.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType import soot.Scene @@ -183,6 +184,7 @@ class Mocker( if (isUtMockAssume(mockInfo)) return false // never mock UtMock.assume invocation if (isUtMockAssumeOrExecuteConcretely(mockInfo)) return false // never mock UtMock.assumeOrExecuteConcretely invocation if (isOverriddenClass(type)) return false // never mock overriden classes + if (type.isInaccessibleViaReflection) return false // never mock classes that we can't process with reflection if (isMakeSymbolic(mockInfo)) return true // support for makeSymbolic if (type.sootClass.isArtificialEntity) return false // never mock artificial types, i.e. Maps$lambda_computeValue_1__7 if (!isEngineClass(type) && (type.sootClass.isInnerClass || type.sootClass.isLocal || type.sootClass.isAnonymous)) return false // there is no reason (and maybe no possibility) to mock such classes diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt index 2d318242a1..7225af9c77 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ObjectWrappers.kt @@ -13,6 +13,7 @@ import org.utbot.engine.overrides.collections.AssociativeArray import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray import org.utbot.engine.overrides.collections.UtHashMap import org.utbot.engine.overrides.collections.UtHashSet +import org.utbot.engine.overrides.security.UtSecurityManager import org.utbot.engine.overrides.strings.UtNativeString import org.utbot.engine.overrides.strings.UtString import org.utbot.engine.overrides.strings.UtStringBuffer @@ -86,6 +87,8 @@ val classToWrapper: MutableMap = putSootClass(java.util.stream.BaseStream::class, UT_STREAM.className) putSootClass(java.util.stream.Stream::class, UT_STREAM.className) // TODO primitive streams https://github.com/UnitTestBot/UTBotJava/issues/146 + + putSootClass(java.lang.SecurityManager::class, UtSecurityManager::class) } /** @@ -187,6 +190,9 @@ private val wrappers = mapOf( wrap(java.util.stream.BaseStream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, wrap(java.util.stream.Stream::class) { _, addr -> objectValue(STREAM_TYPE, addr, CommonStreamWrapper()) }, // TODO primitive streams https://github.com/UnitTestBot/UTBotJava/issues/146 + + // Security-related wrappers + wrap(SecurityManager::class) { type, addr -> objectValue(type, addr, SecurityManagerWrapper()) }, ).also { // check every `wrapped` class has a corresponding value in [classToWrapper] it.keys.all { key -> diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/SecurityManagerWrapper.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/SecurityManagerWrapper.kt new file mode 100644 index 0000000000..90a0be0d1f --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/SecurityManagerWrapper.kt @@ -0,0 +1,39 @@ +package org.utbot.engine + +import org.utbot.engine.overrides.security.UtSecurityManager +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.classId +import org.utbot.framework.util.nextModelName +import soot.Scene +import soot.SootClass +import soot.SootMethod + +class SecurityManagerWrapper : BaseOverriddenWrapper(utSecurityManagerClass.name) { + private val baseModelName: String = "securityManager" + + override fun Traverser.overrideInvoke( + wrapper: ObjectValue, + method: SootMethod, + parameters: List + ): List? { + // We currently do not overload any [SecurityManager] method symbolically + return null + } + + override fun value(resolver: Resolver, wrapper: ObjectValue): UtModel = resolver.run { + val classId = wrapper.type.classId + val addr = holder.concreteAddr(wrapper.addr) + val modelName = nextModelName(baseModelName) + + val instantiationChain = mutableListOf() + val modificationChain = mutableListOf() + return UtAssembleModel(addr, classId, modelName, instantiationChain, modificationChain) + } + + companion object { + val utSecurityManagerClass: SootClass + get() = Scene.v().getSootClass(UtSecurityManager::class.qualifiedName) + } +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index c810d30eb1..f4bdeb29b7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -98,6 +98,7 @@ import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.util.executableId import org.utbot.framework.util.graph +import org.utbot.framework.util.isInaccessibleViaReflection import java.lang.reflect.ParameterizedType import kotlin.collections.plus import kotlin.collections.plusAssign @@ -631,7 +632,7 @@ class Traverser( // so, we have to make an update for the local $r0 - return if (stmt is JAssignStmt) { + return if (stmt is JAssignStmt && stmt.leftOp is JimpleLocal) { val local = stmt.leftOp as JimpleLocal localMemoryUpdate(local.variable to symbolicValue) @@ -1798,6 +1799,8 @@ class Traverser( */ private fun isStaticFieldMeaningful(field: SootField) = !Modifier.isSynthetic(field.modifiers) && + // we don't want to set fields that cannot be set via reflection anyway + !field.fieldId.isInaccessibleViaReflection && // we don't want to set fields from library classes !workaround(IGNORE_STATICS_FROM_TRUSTED_LIBRARIES) { ignoreStaticsFromTrustedLibraries && field.declaringClass.isFromTrustedLibrary() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 4f4f18d119..46fb93856d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -70,7 +70,6 @@ import org.utbot.framework.codegen.model.util.equalTo import org.utbot.framework.codegen.model.util.get import org.utbot.framework.codegen.model.util.inc import org.utbot.framework.codegen.model.util.isAccessibleFrom -import org.utbot.framework.codegen.model.util.isInaccessible import org.utbot.framework.codegen.model.util.length import org.utbot.framework.codegen.model.util.lessThan import org.utbot.framework.codegen.model.util.nullLiteral @@ -139,6 +138,7 @@ import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.stringClassId import org.utbot.framework.plugin.api.util.voidClassId import org.utbot.framework.plugin.api.util.wrapIfPrimitive +import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.framework.util.isUnit import org.utbot.summary.SummarySentenceConstants.TAB import java.lang.reflect.InvocationTargetException @@ -273,7 +273,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - private fun Map.accessibleFields(): Map = filterKeys { !it.isInaccessible } + private fun Map.accessibleFields(): Map = filterKeys { !it.isInaccessibleViaReflection } /** * @return expression for [java.lang.Class] of the given [classId] diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt index aa3bb3f763..e81a9ccdd5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt @@ -22,11 +22,3 @@ fun FieldId.isAccessibleFrom(packageName: String): Boolean { * Whether or not a field can be set without reflection */ fun FieldId.canBeSetIn(packageName: String): Boolean = isAccessibleFrom(packageName) && !isFinal - -private val systemClassId = System::class.id - -/** - * Security field is inaccessible in Runtime even via reflection. - */ -val FieldId.isInaccessible: Boolean - get() = name == "security" && declaringClass == systemClassId diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index c43ae20ba5..fe06a0e5e1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.util.singleExecutableId import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.api.util.withUtContext import org.utbot.framework.plugin.api.withReflection +import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.InvokeInstrumentation @@ -210,15 +211,6 @@ object UtExecutionInstrumentation : Instrumentation { } } - private val inaccessibleViaReflectionFields = setOf( - "security" to "java.lang.System", - "fieldFilterMap" to "sun.reflect.Reflection", - "methodFilterMap" to "sun.reflect.Reflection" - ) - - private val FieldId.isInaccessibleViaReflection: Boolean - get() = (name to declaringClass.name) in inaccessibleViaReflectionFields - private fun sortOutException(exception: Throwable): UtExecutionFailure { if (exception is TimeoutException) { return UtTimeoutException(exception) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt index 2d18f00654..60b490a293 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt @@ -28,6 +28,7 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.longClassId import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.shortClassId +import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.framework.util.valueToClassId import java.lang.reflect.Modifier import java.util.IdentityHashMap @@ -281,7 +282,9 @@ internal class UtModelConstructor( generateSequence(javaClazz) { it.superclass }.forEach { clazz -> val allFields = clazz.declaredFields allFields + .asSequence() .filter { !(Modifier.isFinal(it.modifiers) && Modifier.isStatic(it.modifiers)) } // TODO: what about static final fields? + .filterNot { it.fieldId.isInaccessibleViaReflection } .forEach { it.withAccessibility { fields[it.fieldId] = construct(it.get(value), it.type.id) } } } return utModel diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/ReflectionUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/ReflectionUtils.kt new file mode 100644 index 0000000000..9eb6108f81 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/ReflectionUtils.kt @@ -0,0 +1,30 @@ +package org.utbot.framework.util + +import org.utbot.framework.plugin.api.FieldId +import soot.RefType + +/** + * Several fields are inaccessible in runtime even via reflection + */ +val FieldId.isInaccessibleViaReflection: Boolean + get() { + val declaringClassName = declaringClass.name + return declaringClassName in inaccessibleViaReflectionClasses || + (name to declaringClassName) in inaccessibleViaReflectionFields + } + +val RefType.isInaccessibleViaReflection: Boolean + get() { + return className in inaccessibleViaReflectionClasses + } + +private val inaccessibleViaReflectionClasses = setOf( + "jdk.internal.reflect.ReflectionFactory", + "jdk.internal.reflect.Reflection", + "jdk.internal.loader.ClassLoaderValue", + "sun.reflect.Reflection", +) + +private val inaccessibleViaReflectionFields = setOf( + "security" to "java.lang.System", +) diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt index a72f2b446b..6e31fd4d83 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt @@ -2890,3 +2890,16 @@ inline fun withUsingReflectionForMaximizingCoverage(maximizeCoverage UtSettings.maximizeCoverageUsingReflection = prev } } + +/** + * Run [block] with disabled sandbox in the concrete executor + */ +inline fun withoutSandbox(block: () -> T): T { + val prev = UtSettings.disableSandbox + UtSettings.disableSandbox = true + try { + return block() + } finally { + UtSettings.disableSandbox = prev + } +} diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/exceptions/JvmCrashExamplesTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/exceptions/JvmCrashExamplesTest.kt index d300c01a5e..8f7ed8ff04 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/exceptions/JvmCrashExamplesTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/exceptions/JvmCrashExamplesTest.kt @@ -5,6 +5,9 @@ import org.utbot.examples.DoNotCalculate import org.utbot.examples.eq import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test +import org.utbot.examples.withoutSandbox +import org.utbot.framework.codegen.CodeGeneration +import org.utbot.framework.plugin.api.CodegenLanguage internal class JvmCrashExamplesTest : UtValueTestCaseChecker(testClass = JvmCrashExamples::class) { @Test @@ -17,11 +20,23 @@ internal class JvmCrashExamplesTest : UtValueTestCaseChecker(testClass = JvmCras } @Test - @Disabled("Java 11 transition") fun testCrash() { + withoutSandbox { + check( + JvmCrashExamples::crash, + eq(1), // we expect only one execution after minimization + // It seems that we can't calculate coverage when the child JVM has crashed + coverage = DoNotCalculate + ) + } + } + + @Test + fun testCrashPrivileged() { check( - JvmCrashExamples::crash, + JvmCrashExamples::crashPrivileged, eq(1), // we expect only one execution after minimization + // It seems that we can't calculate coverage when the child JVM has crashed coverage = DoNotCalculate ) } diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt index 3f36a7dfc1..321da17cef 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/strings/StringExamplesTest.kt @@ -26,6 +26,7 @@ internal class StringExamplesTest : UtValueTestCaseChecker( ) ) { @Test + @Disabled("Flaky test: https://github.com/UnitTestBot/UTBotJava/issues/131 (will be enabled in new strings PR)") fun testByteToString() { // TODO related to the https://github.com/UnitTestBot/UTBotJava/issues/131 withSolverTimeoutInMillis(5000) { diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/unsafe/UnsafeOperationsTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/unsafe/UnsafeOperationsTest.kt new file mode 100644 index 0000000000..e44f286e94 --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/unsafe/UnsafeOperationsTest.kt @@ -0,0 +1,41 @@ +package org.utbot.examples.unsafe + +import org.junit.jupiter.api.Test +import org.utbot.examples.DoNotCalculate +import org.utbot.examples.UtValueTestCaseChecker +import org.utbot.examples.eq +import org.utbot.examples.withoutSandbox +import org.utbot.framework.codegen.CodeGeneration +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.MockStrategyApi + +internal class UnsafeOperationsTest : UtValueTestCaseChecker(testClass = UnsafeOperations::class) { + @Test + fun checkGetAddressSizeOrZero() { + withoutSandbox { + check( + UnsafeOperations::getAddressSizeOrZero, + eq(1), + { r -> r!! > 0 }, + // Coverage matcher fails (branches: 0/0, instructions: 15/21, lines: 0/0) + // TODO: check coverage calculation: https://github.com/UnitTestBot/UTBotJava/issues/807 + coverage = DoNotCalculate + ) + } + } + + @Test + fun checkGetAddressSizeOrZeroWithMocks() { + withoutSandbox { + check( + UnsafeOperations::getAddressSizeOrZero, + eq(1), + { r -> r!! > 0 }, + // Coverage matcher fails (branches: 0/0, instructions: 15/21, lines: 0/0) + // TODO: check coverage calculation: https://github.com/UnitTestBot/UTBotJava/issues/807 + coverage = DoNotCalculate, + mockStrategy = MockStrategyApi.OTHER_CLASSES + ) + } + } +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt index 0d4531e804..d3a585ff00 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcess.kt @@ -48,14 +48,21 @@ private fun log(any: Any?) { private val kryoHelper: KryoHelper = KryoHelper(System.`in`, System.`out`) +/** + * Command-line option to disable the sandbox + */ +const val DISABLE_SANDBOX_OPTION = "--disable-sandbox" + /** * It should be compiled into separate jar file (child_process.jar) and be run with an agent (agent.jar) option. */ -fun main() { - permissions { - // Enable all permissions for instrumentation. - // SecurityKt.sandbox() is used to restrict these permissions. - + AllPermission() +fun main(args: Array) { + if (!args.contains(DISABLE_SANDBOX_OPTION)) { + permissions { + // Enable all permissions for instrumentation. + // SecurityKt.sandbox() is used to restrict these permissions. + +AllPermission() + } } // We don't want user code to litter the standard output, so we redirect it. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt index 334f19cc4d..d57ca98033 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/ChildProcessRunner.kt @@ -49,7 +49,14 @@ class ChildProcessRunner { val directory = WorkingDirService.provide().toFile() - val processBuilder = ProcessBuilder(cmds) + val commandsWithOptions = buildList { + addAll(cmds) + if (UtSettings.disableSandbox) { + add(DISABLE_SANDBOX_OPTION) + } + } + + val processBuilder = ProcessBuilder(commandsWithOptions) .redirectError(errorLogFile) .directory(directory) diff --git a/utbot-sample/src/main/java/org/utbot/examples/exceptions/JvmCrashExamples.java b/utbot-sample/src/main/java/org/utbot/examples/exceptions/JvmCrashExamples.java index a753e86751..7ecba1ea68 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/exceptions/JvmCrashExamples.java +++ b/utbot-sample/src/main/java/org/utbot/examples/exceptions/JvmCrashExamples.java @@ -1,6 +1,9 @@ package org.utbot.examples.exceptions; import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + import sun.misc.Unsafe; public class JvmCrashExamples { @@ -13,7 +16,6 @@ public int exit(int i) { return i; } - // this method crashes JVM public int crash(int i) throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); @@ -26,4 +28,25 @@ public int crash(int i) throws Exception { return 1; } + + // this method crashes JVM and uses [AccessController.doPrivileged] to obtain privileges + public int crashPrivileged(int i) { + return AccessController.doPrivileged((PrivilegedAction) () -> { + Field f; + try { + f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + unsafe.putAddress(0, 0); + + if (i == 0) { + return i; + } + + return 1; + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } } diff --git a/utbot-sample/src/main/java/org/utbot/examples/unsafe/UnsafeOperations.java b/utbot-sample/src/main/java/org/utbot/examples/unsafe/UnsafeOperations.java new file mode 100644 index 0000000000..7b76b0ba13 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/unsafe/UnsafeOperations.java @@ -0,0 +1,20 @@ +package org.utbot.examples.unsafe; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class UnsafeOperations { + public int getAddressSizeOrZero() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + return unsafe.addressSize(); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Reflection failed"); + } + } +}