[Soot-list] Problem Instrumenting Kotlin data class

18 views
Skip to first unread message

Alessio Gambi

unread,
Mar 22, 2021, 1:15:19 PM3/22/21
to soot...@cs.mcgill.ca
Dear All,

I am facing a weird problem while instrumenting a Kotlin data class using soot [1].

The problem is that after instrumenting the class, the byte code verifier rejects the class.

The method causing this seems to be “copy” that contains those units:

r0 := @this: com.orgzly.android.util.Encoding
$r1 := @parameter0: java.lang.String
$r2 := @parameter1: java.lang.String
$r3 := @parameter2: java.lang.String
staticinvoke <kotlin.jvm.internal.Intrinsics: void checkNotNullParameter(java.lang.Object,java.lang.String)>($r1, "used")
r0 = new com.orgzly.android.util.Encoding
specialinvoke r0.<com.orgzly.android.util.Encoding: void <init>(java.lang.String,java.lang.String,java.lang.String)>($r1, $r2, $r3)
return r0

The same method after the instrumentation looks like the one reported below (I simplified it), where the units marked with ** are the code injected via soot.

The instrumentations 
- encapsulates all the parameters of method calls into generic arrays of objects
- invokes a Monitor class to track method invocations (before/after) and captures return values
- wraps the method with a generic try-catch-all where the catch block logs the captured exception before rethrowing it

Now the weirdness (IMHO) is that r0 is assigned first to @this and then to something different (r0 = new com.orgzly.android.util.Encoding).
This is part of the original code (see above), but I am afraid it might break the instrumented code because we pass r0 as parameter to other invocations in the catch block,
under the assumption it points to “this”, while in reality it points to another instance of the same type.
Consequently, the bytecode verifier reports the first call using it  in the catch block (staticinvoke <Monitor: void onAppMethodCaptureException)  as invalid (with type conflict)

Maybe someone has experiences something similar and/or can suggest a work around? Assuming this is indeed the problem, the only option I see at the moment
 is to assign the return of  r0 = new com.orgzly.android.util.Encoding to a fresh local variable (rX)

Any help is appreciated !

Best

— Dr. Alessio Gambi
Chair for SE 2
University of Passau


—————

INSTRUMENTED CODE:
                 r0 := @this: com.orgzly.android.util.Encoding
                 $r1 := @parameter0: java.lang.String
                 $r2 := @parameter1: java.lang.String
                 $r3 := @parameter2: java.lang.String
**               $r5 = newarray (java.lang.Object)[3]
**               $r5[0] = $r1
**               $r5[1] = $r2
**               $r5[2] = $r3
**               staticinvoke <Monitor: void onAppMethodCall(java.lang.String,java.lang.Object,java.lang.String,java.lang.Object[])>("com.orgzly", r0, "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", $r5)
**               $r6 = newarray (java.lang.Object)[2]
**               $r6[0] = $r1
**               $r6[1] = "used"
**               staticinvoke <Monitor: void onLibMethodCall(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object[])>(null, "<kotlin.jvm.internal.Intrinsics: void checkNotNullParameter(java.lang.Object,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", $r6)
                 staticinvoke <kotlin.jvm.internal.Intrinsics: void checkNotNullParameter(java.lang.Object,java.lang.String)>($r1, "used")
**               staticinvoke <Monitor: void onLibMethodReturnNormally(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object)>(null, "<kotlin.jvm.internal.Intrinsics: void checkNotNullParameter(java.lang.Object,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", null)

                 r0 = new com.orgzly.android.util.Encoding
                 specialinvoke r0.<com.orgzly.android.util.Encoding: void <init>(java.lang.String,java.lang.String,java.lang.String)>($r1, $r2, $r3)
**               staticinvoke <Monitor: void onAppMethodReturnNormally(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object)>(r0, "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", r0)
                 return r0

**               $r0 := @caughtexception
**               $r7 = $r0
**               staticinvoke <Monitor: void onAppMethodCaptureException(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object)>(r0, "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", $r0)
**               $r4 = $r0
**               staticinvoke <Monitor: void onAppMethodThrowException(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object)>(r0, "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", $r4)
**               staticinvoke <Monitor: void onAppMethodReturnExceptionally(java.lang.Object,java.lang.String,java.lang.String,java.lang.Object)>(r0, "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", "<com.orgzly.android.util.Encoding: com.orgzly.android.util.Encoding copy(java.lang.String,java.lang.String,java.lang.String)>", $r4)
**               throw $r4


Alessio Gambi

unread,
Mar 23, 2021, 3:44:22 AM3/23/21
to Marc Miltenberger, soot...@cs.mcgill.ca
Hi Marc,

maybe you are right, but the same pattern of code works fine with other apps.

Best

— Alessio 

On 22. Mar 2021, at 18:29, Marc Miltenberger <marcmilt...@gmail.com> wrote:

Hello Dr. Gambi,

I see one issue in this line:
$r0 := @caughtexception

This does not seem right since $r0 is of type com.orgzly.android.util.Encoding. This should actually be some sort of throwable.

Best regards,
Marc Miltenberger



_______________________________________________
Soot-list mailing list
Soot...@CS.McGill.CA
https://mailman.CS.McGill.CA/mailman/listinfo/soot-list

Marc Miltenberger

unread,
Mar 23, 2021, 9:03:00 AM3/23/21
to Alessio Gambi, soot...@cs.mcgill.ca
Hello Dr. Gambi,

I see one issue in this line:
$r0 := @caughtexception

This does not seem right since $r0 is of type com.orgzly.android.util.Encoding. This should actually be some sort of throwable.

Best regards,
Marc Miltenberger



Am Mo., 22. März 2021 um 18:24 Uhr schrieb Alessio Gambi <alessi...@uni-passau.de>:

Alessio Gambi

unread,
Mar 23, 2021, 1:02:00 PM3/23/21
to soot...@cs.mcgill.ca, Marc Miltenberger
One follow up to this:

We cloned and rebuild soot from source, but commented out this line [1] inside DexBody.java, and this solved the problem.

Basically, we avoid that the LocalPacker transforms the jimple body to minimize the use of local variables.

I am not sure this is the best strategy in general, but I am also not sure how LocalPacker can be disabled without patching the code.

Maybe I should run some transformation after I inject the tracing code to ensure this local variable is not reused? Something like LocalSplitter?

Any thoughts are welcome.

Best


— Alessio

Reply all
Reply to author
Forward
0 new messages