Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing case in decompiled switch statement #359

Open
ambergorzynski opened this issue Aug 23, 2024 · 0 comments
Open

Missing case in decompiled switch statement #359

ambergorzynski opened this issue Aug 23, 2024 · 0 comments
Labels

Comments

@ambergorzynski
Copy link

CFR version

CFR 0.153-SNAPSHOT (3d1d0f4)

Compiler

OpenJDK 19.0.2 on Ubuntu 22.04

Description

There appears to be an error decompiling a small program. I am doing some work using Java bytecode, so I’m conscious that this is not a ‘normal’ Java program and therefore may be outside of the scope of CFR! In particular it has some spicy control flow. But it would be interesting to know what is causing the issue since some minor changes to the program (that do not eliminate the weird control flow) result in a correctly decompiled program.

In the original program, the first switch statement has a case 0 that jumps to block_2. In the decompiled program, the first switch statement only has case 1 and case 2.

I attempted to simplify the example program further to pinpoint the source of the issue, but it either resolves (e.g. removing the switch case 2 in the first switch statement results in correct decompilation), or fails to decompile. I will report the related failed decompilation example separately in case it is useful for diagnostics.

Excerpt from bytecode program (full files for reproduction are attached below):

.method public testCase([I)V
    .limit stack 3
    .limit locals 2

block_0:

    ; store block label in output array
    aload_1
    bipush 0
    bipush 0
    iastore

    bipush 0
    lookupswitch
        0: block_2
        1: block_1
        2: block_3
        default : block_2

block_1: 
    ; store block label in output array
    aload_1
    bipush 1
    bipush 1
    iastore

    bipush 0
    lookupswitch
        0: block_3
        1: block_1
        default : block_2

block_2: 
    ; store block label in output array
    aload_1
    bipush 1
    bipush 2
    iastore

    return
            
block_3: 
    return
        
.end method

Corresponding excerpt from decompiled program, with comments indicating the relevant switch case added by me:

public class TestCase {
    /*
     * Enabled aggressive block sorting
     */
    public void testCase(int[] nArray) {
   
        // Start of code corresponding to block_0 
        nArray[0] = 0;
        switch (0) {
            case 1: {
                break;
            }
            case 2: {
                return;
            }
        }
        // End of code corresponding to block_0 

        block8: while (true) {
            nArray[1] = 1;
            switch (0) {
                case 1: {
                    continue block8;
                }
                default: {
                    nArray[1] = 2;
                    return;
                }
                case 0: 
            }
            break;
        }
    }
}

Example

example.zip

To illustrate the issue, I attach some small programs that call the function in its original and decompiled form and prints the contents of the output array. In the original function the output array contains [0, 2] after the function call, while in the decompiled version the output array contains [0, 1].

  • TestCase.j is the original java bytecode program
  • TestCase.class is the compiled class file from TestCase.j
  • TestCase.java is the decompiled output from CFR
  • Wrapper.java is a wrapper program to show the output from the original and decompiled programs
  • example.sh a shell script with the steps for running the original and decompiled examples

To run:

  • Get Jasmin jar from here
  • Get CFR 0.153-SNAPSHOT (3d1d0f4)
  • Edit the paths in lines 1 and 2 of the shell script as appropriate
  • Run the script; you should see [0, 2] as output from the original program and [0, 1] as output from the de- and re-compiled program indicating the missing switch case
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant