soot-oss / soot

Soot - A Java optimization framework
GNU Lesser General Public License v2.1
2.89k stars 708 forks source link

use-original-names RuntimeException "no defs for value" #90

Open malaverdiere opened 11 years ago

malaverdiere commented 11 years ago

When running Soot on jasper-runtime-5.5.23.jar, the following exception occurs only if use-original-names is enabled:

Exception in thread "main" java.lang.RuntimeException: no defs for value: l5! in <org.apache.jasper.runtime.ProtectedFunctionMapper: void mapFunction(java.lang.String,java.lang.Class,java.lang.String,java.lang.Class[])>
at soot.Body.validateUses(Body.java:336)
at soot.Body.validate(Body.java:233)
at soot.jimple.JimpleBody.validate(JimpleBody.java:66)
at soot.PackManager.runBodyPacks(PackManager.java:834)
at soot.PackManager.runBodyPacks(PackManager.java:516)
at soot.PackManager.runBodyPacks(PackManager.java:423)
at soot.PackManager.runPacksNormally(PackManager.java:400)
at soot.PackManager.runPacks(PackManager.java:341)
at soot.Main.run(Main.java:198)
at soot.Main.main(Main.java:141)
at Main.main(Main.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

The body of the method is:

this := @this: org.apache.jasper.runtime.ProtectedFunctionMapper
fnQName := @parameter0: java.lang.String
c := @parameter1: java.lang.Class
methodName := @parameter2: java.lang.String
args := @parameter3: java.lang.Class[]
$z0 = staticinvoke <org.apache.jasper.security.SecurityUtil: boolean isPackageProtectionEnabled()>()
if $z0 == 0 goto virtualinvoke c.<java.lang.Class: java.lang.reflect.Method getDeclaredMethod(java.lang.String,java.lang.Class[])>(methodName, args)
$r0 = new org.apache.jasper.runtime.ProtectedFunctionMapper$2
specialinvoke $r0.<org.apache.jasper.runtime.ProtectedFunctionMapper$2: void <init>(org.apache.jasper.runtime.ProtectedFunctionMapper,java.lang.Class,java.lang.String,java.lang.Class[])>(this, c, methodName, args)
$r1 = staticinvoke <java.security.AccessController: java.lang.Object doPrivileged(java.security.PrivilegedExceptionAction)>($r0)
method = (java.lang.reflect.Method) $r1
goto [?= $r9 = this.<org.apache.jasper.runtime.ProtectedFunctionMapper: java.util.HashMap fnmap>]
$r2 := @caughtexception
ex = $r2
$r3 = new java.lang.RuntimeException
$r4 = new java.lang.StringBuffer
specialinvoke $r4.<java.lang.StringBuffer: void <init>()>()
$r4 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>("Invalid function mapping - no such method: ")
$r5 = virtualinvoke ex.<java.security.PrivilegedActionException: java.lang.Exception getException()>()
$r6 = virtualinvoke $r5.<java.lang.Exception: java.lang.String getMessage()>()
$r4 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>($r6)
$r7 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.String toString()>()
specialinvoke $r3.<java.lang.RuntimeException: void <init>(java.lang.String)>($r7)
throw $r3
virtualinvoke c.<java.lang.Class: java.lang.reflect.Method getDeclaredMethod(java.lang.String,java.lang.Class[])>(methodName, args)
goto [?= $r9 = this.<org.apache.jasper.runtime.ProtectedFunctionMapper: java.util.HashMap fnmap>]
$r8 := @caughtexception
e = $r8
$r3 = new java.lang.RuntimeException
$r4 = new java.lang.StringBuffer
specialinvoke $r4.<java.lang.StringBuffer: void <init>()>()
$r4 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>("Invalid function mapping - no such method: ")
$r6 = virtualinvoke e.<java.lang.NoSuchMethodException: java.lang.String getMessage()>()
$r4 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>($r6)
$r7 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.String toString()>()
specialinvoke $r3.<java.lang.RuntimeException: void <init>(java.lang.String)>($r7)
throw $r3
$r9 = this.<org.apache.jasper.runtime.ProtectedFunctionMapper: java.util.HashMap fnmap>
virtualinvoke $r9.<java.util.HashMap: java.lang.Object put(java.lang.Object,java.lang.Object)>(fnQName, l5)
return
malaverdiere commented 11 years ago

I forgot to attach the sources and the jars URLs http://mirrors.ibiblio.org/pub/mirrors/maven2/jetty/javax.servlet/5.1.11/javax.servlet-5.1.11.jar http://mirrors.ibiblio.org/pub/mirrors/maven2/tomcat/jasper-runtime/5.5.23/jasper-runtime-5.5.23.jar http://mirrors.ibiblio.org/pub/mirrors/maven2/tomcat/commons-logging/4.0.6/commons-logging-4.0.6.jar http://mirrors.ibiblio.org/pub/mirrors/maven2/tomcat/commons-el/5.5.23/commons-el-5.5.23.jar

Trans.java:

import soot.*;
import soot.jimple.*;
import soot.jimple.toolkits.base.Aggregator;
import soot.jimple.toolkits.scalar.ConstantPropagatorAndFolder;
import soot.jimple.toolkits.scalar.CopyPropagator;
import soot.jimple.toolkits.scalar.DeadAssignmentEliminator;
import soot.jimple.toolkits.scalar.NopEliminator;
import soot.toolkits.graph.*;
import soot.toolkits.scalar.LocalPacker;
import soot.toolkits.scalar.SimpleLocalDefs;
import soot.toolkits.scalar.UnusedLocalEliminator;
import soot.tagkit.LineNumberTag;
import soot.tagkit.SourceLineNumberTag;
import soot.tagkit.SourceLnNamePosTag;
import soot.tagkit.SourceLnPosTag;
import soot.tagkit.Tag;
import soot.util.Chain;

import java.util.*;

//import org.apache.jasper.runtime.JspRuntimeLibrary;

public class Trans extends BodyTransformer{

protected void internalTransform(Body body, String phaseName, Map options)   
{
    if (body.getMethod().getDeclaringClass().toString().compareTo("org.apache.jasper.runtime.ProtectedFunctionMapper") == 0 && body.getMethod().getName().compareTo("mapFunction") == 0)
    {
        //System.out.println("====================================================================================");           
        //System.out.println("\nInstrumenting " + body.getMethod().getSignature());
        //System.out.println(body);
            //System.out.println("====================================================================================");
    }
}

}

Main.java:

import soot.Pack;
import soot.PackManager;
import soot.Scene;
import soot.SootClass;
import soot.Transform;

/* Soot - a J*va Optimization Framework
* Copyright (C) 2008 Eric Bodden
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/

public class Main { 

    public static void main(String[] args) 
    {
    final String jarDir = "/home/marc-andre/MinimalOriginalNameException/";

        args = new String[] { 
                "-exclude", "org.apache.jasper.runtime.",
                "-no-bodies-for-excluded", 
                "-p", "jb", "use-original-names:true",
                "-p", "tag.ln", "enabled",
                "-keep-line-number", 
                "-src-prec", "class",
                "-validate",
        "-prepend-classpath",
                "-cp", jarDir+"/javax.servlet-5.1.11.jar;"+jarDir+"/commons-el-5.5.23.jar;"+jarDir+"tcommons-logging-4.0.6.jar;",
                "-f", "class",  
                "-process-dir", "/home/marc-andre/MinimalOriginalNameException/jasper-runtime-5.5.23.jar"
        };                           
        Pack jtp = PackManager.v().getPack("jtp");
    jtp.add(new Transform("jtp.trans", new Trans()));
    Scene.v().addBasicClass("java.lang.StringBuilder", SootClass.SIGNATURES);
    soot.Main.main(args);           
    }   
}               
quentin commented 11 years ago

The dump of the LocalVariableTable for the particular method will probably show that original names are not correctly reported by the compiler at the bytecode where l5 shows up.

malaverdiere commented 11 years ago

I have added the following code in coffi.Util.resolveFromClassFile:

        if (aClass.getName().equals("org.apache.jasper.runtime.ProtectedFunctionMapper")){
            boolean foundLVT = false;
            for (attribute_info ai: coffiClass.findMethod("mapFunction").attributes){
                if (ai instanceof LocalVariableTable_attribute){
                    System.err.println("org.apache.jasper.runtime.ProtectedFunctionMapper mapFunction:");
                    System.err.println(ai.toString());
                    foundLVT = false;
                }
                if (!foundLVT)
                    System.err.println("+++++++++++++++++++ WARNING WARNING WARNING Local Variable Table is Empty ++++++++++++++++++++++");
            }
        }

I am getting that the local variable table is empty.

quentin commented 11 years ago

Hum, how can Coffi retrieve the other names then ?

I think it should be located as a sub-attribute of the CodeAttribute of the class. Here is how it is located in coffi:

Code_attribute ca = method.locate_code_attribute();
LocalVariableTable_attribute la = ca.findLocalVariableTable();
malaverdiere commented 11 years ago

Am I trying to retrieve that information too early?

quentin commented 11 years ago

No, sorry my remark was a bit sarcastic: if the local variable table is empty, how can we explain all the original names we see in the jimple code.

Can you try something like

coffiClass.findMethod("mapFunction").locate_code_attibute().findLocalVariableTable();
malaverdiere commented 11 years ago

I am getting this result

-------------------------- Local variable table: --------------------------
start: 26length: 3name_index: 54descriptor_index: 41index: 5
start: 31length: 34name_index: 55descriptor_index: 56index: 6
start: 74length: 3name_index: 54descriptor_index: 41index: 5
start: 79length: 31name_index: 57descriptor_index: 58index: 6
start: 0length: 122name_index: 47descriptor_index: 48index: 0
start: 0length: 122name_index: 59descriptor_index: 60index: 1
start: 0length: 122name_index: 61descriptor_index: 62index: 2
start: 0length: 122name_index: 63descriptor_index: 60index: 3
start: 0length: 122name_index: 64descriptor_index: 65index: 4

I am putting this chunk of code at the bottom of the method:

    if (aClass.getName().equals("org.apache.jasper.runtime.ProtectedFunctionMapper")){
    LocalVariableTable_attribute lvt = coffiClass.findMethod("mapFunction").locate_code_attribute().findLocalVariableTable();
    if (lvt.local_variable_table_length == 0)
        System.err.println("-------------------------- No local variable table -------------------");
    else{
        System.err.println("-------------------------- Local variable table: --------------------------");
        System.err.println(lvt.toString());
    }
    }
quentin commented 11 years ago

The best would be to look at the bytecode of the class with "javac -c -p", it pretty-prints that part quite well.

malaverdiere commented 11 years ago

Here is for the whole method

public void mapFunction(java.lang.String, java.lang.Class, java.lang.String, java.lang.Class[]);
  Code:
0: invokestatic  #5                  // Method org/apache/jasper/security/SecurityUtil.isPackageProtectionEnabled:()Z
3: ifeq          65
6: new           #12                 // class org/apache/jasper/runtime/ProtectedFunctionMapper$2
9: dup           
10: aload_0       
11: aload_2       
12: aload_3       
13: aload         4
15: invokespecial #13                 // Method org/apache/jasper/runtime/ProtectedFunctionMapper$2."<init>":(Lorg/apache/jasper/runtime/ProtectedFunctionMapper;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)V
18: invokestatic  #14                 // Method java/security/AccessController.doPrivileged:(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;
21: checkcast     #15                 // class java/lang/reflect/Method
24: astore        5
26: goto          110
29: astore        6
31: new           #17                 // class java/lang/RuntimeException
34: dup           
35: new           #18                 // class java/lang/StringBuffer
38: dup           
39: invokespecial #19                 // Method java/lang/StringBuffer."<init>":()V
42: ldc           #20                 // String Invalid function mapping - no such method: 
44: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
47: aload         6
49: invokevirtual #22                 // Method java/security/PrivilegedActionException.getException:()Ljava/lang/Exception;
52: invokevirtual #23                 // Method java/lang/Exception.getMessage:()Ljava/lang/String;
55: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
58: invokevirtual #24                 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
61: invokespecial #25                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
64: athrow        
65: aload_2       
66: aload_3       
67: aload         4
69: invokevirtual #26                 // Method java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
72: astore        5
74: goto          110
77: astore        6
79: new           #17                 // class java/lang/RuntimeException
82: dup           
83: new           #18                 // class java/lang/StringBuffer
86: dup           
87: invokespecial #19                 // Method java/lang/StringBuffer."<init>":()V
90: ldc           #20                 // String Invalid function mapping - no such method: 
92: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
95: aload         6
97: invokevirtual #28                 // Method java/lang/NoSuchMethodException.getMessage:()Ljava/lang/String;
  100: invokevirtual #21                 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
  103: invokevirtual #24                 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
  106: invokespecial #25                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
  109: athrow        
  110: aload_0       
  111: getfield      #3                  // Field fnmap:Ljava/util/HashMap;
  114: aload_1       
  115: aload         5
  117: invokevirtual #29                 // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  120: pop           
  121: return        
  Exception table:
from    to  target type
    6    26    29   Class java/security/PrivilegedActionException
    65    74    77   Class java/lang/NoSuchMethodException
quentin commented 11 years ago

I just remembered the -verbose option is necessary too:

LineNumberTable:
    line 95: 0
    line 97: 6
    line 107: 26
    line 103: 29
    line 104: 31
    line 110: 65
    line 115: 74
    line 111: 77
    line 112: 79
    line 118: 110
    line 119: 121
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
          26       3     5 method   Ljava/lang/reflect/Method;
          31      34     6    ex   Ljava/security/PrivilegedActionException;
          74       3     5 method   Ljava/lang/reflect/Method;
          79      31     6     e   Ljava/lang/NoSuchMethodException;
           0     122     0  this   Lorg/apache/jasper/runtime/ProtectedFunctionMapper;
           0     122     1 fnQName   Ljava/lang/String;
           0     122     2     c   Ljava/lang/Class;
           0     122     3 methodName   Ljava/lang/String;
           0     122     4  args   [Ljava/lang/Class;
quentin commented 11 years ago

As you can see, local 5 doesn't have a name defined at bytecode position 115. Unfortunately, the same def/use local 5 appears at different positions with the name "method", thus the current local naming method creates disjoints Local.