Sunday, July 7, 2019

PowerMock indirectly allows setAccessible on any method

The CS class I'm on staff for is looking into ways to grade Java Android app-based assignments securely. Restricting student code without breaking Robolectric is tricky - we need to allow e.g. reflection when done by Robolectric or the test suites but not when done by student code. Some of our test suites use PowerMock, which of course internally exercises all kinds of permissions. Unfortunately it could be used by student code to override access modifiers and call arbitrary methods: WhiteboxImpl provides the public function getAllMethods, which makes all a class's methods accessible and returns them.

So checking whether trusted frameworks are using the dangerous permissions is insufficient. Fortunately, we make PowerMock a testImplementation dependency, so it's not on the compile classpath for the student sources. While students could access it at runtime via reflection (the problematic method is public), our SecurityManager can check whether PowerMock is being invoked reflectively and deny permission if so.

Saturday, July 6, 2019

Java MethodHandles can work like reflection

I'm helping test a Java execution sandbox. We want to allow untrusted code to use streams, so we can't deny the suppressAccessChecks and accessDeclaredMembers permissions, so instead we made the classloader for untrusted code reject anything in the java.lang.reflect package. Today I found that the MethodHandles API allows essentially the same capabilities as reflection. A lookup object obtained through privateLookupIn on a class belonging to a classloader that is an ancestor of the sandbox classloader can bypass the class lookup restrictions. Handles can then be obtained to normal reflection API methods, which can be used to override access modifiers.

Blocking the entire java.lang.invoke package isn't viable because streams/lambdas need parts of it, but blocking MethodHandles and MethodHandles.Lookup should make it impossible to dynamically invoke arbitrary methods.

Friday, July 5, 2019

When Kotlin tests fail with "class not found" in Android Studio

Today I was setting up a new Android Studio project. For reasons, the app had to be in Java, but I wanted to write the test suite in Kotlin. The test class and test methods had the run buttons in the margin, and the build appeared to go smoothly, but trying to run them produced a "class not found" error instead of test results. It turns out I had forgotten to apply the kotlin-android plugin. (That plugin comes from the usual Kotlin buildscript dependency.) It's also important to add the Kotlin standard library as a normal dependency, as testImplementation in my case.

Calling MethodHandle#invoke from Kotlin

Java has a class called MethodHandle which represents a dynamic but strongly typed operation. It provides invoke and invokeExact methods that are "signature polymorphic" and handled specially: rather than treating their invocations as the use of a normal varargs method, the compiler emits bytecode involving the invokedynamic instruction.

Since calls to these methods require compiler support, other JVM language compilers may not handle them properly. Kotlin 1.3.x doesn't generate the special bytecode by default and therefore cannot call signature-polymorphic methods correctly. However, the correct behavior can be enabled with the -XXLanguage:+PolymorphicSignature argument to the compiler. Alternatively, the similar invokeWithArguments method can be called normally, but may be a little slower due to the extra work of transforming the normal varargs call into a dynamic invocation.

Wednesday, July 3, 2019

Fixing the signing plugin on Gradle 5.1 and later

I previously found that using the maven-publish and signing Gradle plugins to publish to Maven Central did not work on Gradle versions 5.1 and newer: the signArchives task failed with a "duplicate key" error. So for a while I had to use the Gradle 5.0 wrapper to deploy. More recently I decided to investigate more thoroughly, and came up with a fix. Conveniently, the list of signatures is available from the signing task. Rather than this simple configuration block...
signing {
    sign configurations.archives
}

...I remove duplicates...
signing {
    def signTasks = sign configurations.archives
    signTasks.each { t ->
        def signatures = t.getSignatures()
        signatures.removeAll { oneSig ->
            signatures.count { anotherSig -> oneSig.getClassifier() == anotherSig.getClassifier() } > 1
        }
    }
}

...allowing the signing and upload to complete successfully.

The Robolectric RuntimeEnvironment isn't accessible from a different classloader

Today I worked on a SecurityManager that oversees the execution of Robolectric tests. In one check, I needed to consult the RuntimeEnvironment of the test. Trying to simply use members of that class gave wrong results; it appeared that no tests had been started yet. The problem was a difference of classloader. Robolectric loads reloads tests into a "sandbox" class loader to provide SDK isolation. Each loader got its own RuntimeEnvironment with its own set of static members. Since the SecurityManager was loaded in the system loader, it always referred to the copy in the system loader, which isn't used for tests. The solution was to go through the class context to get the test class loader, then use reflection on that to get the desired properties.

Tuesday, July 2, 2019

Robolectric's sandbox classloader doesn't preserve the ProtectionDomain

Today I investigated why my organization had been having trouble applying a ProGrade security policy to Robolectric tests. The policy we used for previous projects granted and denied rights to the untrusted code using codeBase directives, but they seemed to have no effect. It turns out that Robolectric "sandboxes" code by SDK version by reloading classes into different classloaders. The sandbox classloader doesn't seem to preserve the ProtectionDomain/CodeSource of the sandboxed classes, so the information about their origin in an untrusted location is lost. Since we control which directory structures (and therefore packages) can be compiled, I'm working on writing a SecurityManager that discriminates by class/package.