Search code examples
androidbytecodedalvikbytecode-manipulationsmali

Reference vs. Precise Reference in Dalvik Verifier


I am writing instrumentation on Dalvik bytecode which performs some logging for various method call entries. Specifically, at various method call sites, I will insert a set of instructions which collects up the parameters, puts them in an Object[] array, and then passes that to a logging function.

This is all fine and well, I have implemented and gotten past all of the kludges for most apps. But I'm encountering one particularly impenetrable Dalvik verifier error:

java.lang.VerifyError: Verifier rejected class io.a.a.g: void io.a.a.g.r() 
failed to verify: void io.a.a.g.r(): [0x570] register v5 has type Reference: 
java.lang.Object but expected Precise Reference: java.lang.String

I looked at the code that is being generated by my instrumentation, and all I'm doing is putting register v5 in an array of objects.

I have a few questions here:

  • What is a precise reference, and why is it incompatible with references?
  • What does the offset here mean? [0x570] points into the middle of a bytecode instruction, so it doesn't clearly map to any instructions: the instructions around there don't involve v5.
  • How would I go about debugging this? Ideally, I'd like to know what the verifier thinks should be happening and fix that.

EDIT:

Here's a dump of the bytecode of the method I'm speaking about. https://gist.github.com/kmicinski/c8382f0521b19643bb24379d91c47d36 As you can see, 0x570 isn't the beginning of an instruction, and (as far as I can tell) there isn't any place where r5 conflicts with a String where it should be an object.


Solution

  • If you look closely at the error, it is telling you that you're passing an Object, where a String is expected. Anyway, there isn't much more that can be said unless you post the actual bytecode that is causing the problem.

    Are you sure that 0x570 points to the middle of an instruction? It shouldn't. Anyway, the way you would go about debugging it is to look at the relevant instruction and figure out why r5 is an Object when it's supposed to be a String. Or you could post the bytecode so I could take a look.

    Edit: Now that you've posted the code, there is in fact a path which results in v5 being Object, but it is a bit subtle

    The exception handler .catch JSONException {:5D8 .. :938} :BDE jumps to :BDE

    The code for the exception handler stores the caught exception in v5, meaning that v5 is no longer a String at this point. It then jumps to :162

    :BDE
    00000BDE  move-exception      v5
    00000BE0  const               v0, 0x00488B36
    00000BE6  invoke-static       Logger->logBasicBlockEntry(I)V, v0
    00000BEC  goto/16             :162
    

    :162 is within the range of another exception handler: .catch ClassNotFoundException {:2E .. :594} :BF0

    :Bf0 leaves v5 untouched and jumps to :A28

    :BF0
    00000BF0  move-exception      v6
    00000BF2  const               v0, 0x00488B3E
    00000BF8  invoke-static       Logger->logBasicBlockEntry(I)V, v0
    00000BFE  goto/16             :A28
    

    :A28 is the beginning of a code block which assumes that v5 is String. In particular, on instruction :AE0, v5 is passed to a function taking a String.

    00000AE0  invoke-virtual      StringBuilder->append(String)StringBuilder, v7, v5
    

    0xAE0 is exactly twice 0x570, which explains the offset shown in the error, once you adjust for code units as JesusFreke suggested.

    Note that this isn't necessarily the only broken code path, it's just the first one I found while looking through your code. However, one bad path is sufficient to unify v5's type with JSONException and hence turn it into Object.