javapathfinder / jpf-core

JPF is an extensible software analysis framework for Java bytecode. jpf-core is the basis for all JPF projects; you always need to install it. It contains the basic VM and model checking infrastructure, and can be used to check for concurrency defects like deadlocks, and unhandled exceptions like NullPointerExceptions and AssertionErrors.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Lambda expression fails to capture a reference type local variable in JPF on Java 11

quadhier opened this issue · comments

Problem

This program will cause JPF on Java 11 (java-10-gradle branch) to crash:

interface I {
  void foo();
}

public class Test {
  public static void main (String[] args) {
    String s = "abc";
    I i = () -> {
      System.out.println(s);
    }
    i.foo();
  }
}

Bug Diagnosis

The following code snippet sets wrong argument for a reference type free variable of a lambda expression.

int val = ((ElementInfo) freeVarValues[i]).getObjectRef() + 1;
// + 1 because when object is created ( i.e GenericHeap.createObject(...)) the value of objRef is initialized
// to the NamedField value in ElementInfo. But the value needed here is the value of arrayField which
// NamedField value +1. This is because both array and object fields are created in GenericHeap.newString().
fields.setReferenceValue(i, val);

A Tentative Fix

--- a/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java
+++ b/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java
@@ -294,11 +294,8 @@ public class FunctionObjectFactory {
         if (freeVarValues[i] == null) {
           fields.setReferenceValue(i, MJIEnv.NULL);
         } else {
-          int val = ((ElementInfo) freeVarValues[i]).getObjectRef() + 1;
-          // + 1 because when object is created ( i.e GenericHeap.createObject(...)) the value of objRef is initialized
-          // to the NamedField value in ElementInfo. But the value needed here is the value of arrayField which
-          // NamedField value +1. This is because both array and object fields are created in GenericHeap.newString().
-          fields.setReferenceValue(i, val);
+          int objRef = ((ElementInfo) freeVarValues[i]).getObjectRef();
+          fields.setReferenceValue(i, objRef);
         }
       }
     }

The comment in the old version of the code suggests that the +1 is sometimes needed. Can you please check if other cases fail when removing that? Perhaps we need to check the type of object that is created?

The comment in the old version of the code suggests that the +1 is sometimes needed. Can you please check if other cases fail when removing that?

Hi @cyrille-artho, I have confimed that this change won't bring regressions. Here is some additional information:

This snippet of code is imported in this commit and the comment is added in this commit. Both of them are parts of PR #210. And what they are doing is changing implementation of setFuncObjFields() to fix invokedynamic for string concat (the long comment also somewhat implies this).

But as the source code of JPF changes, in the current version of JPF, setFuncObjFields() won't be called in the case of string concat (which is shown below). So I think this change is safe.

//It does not make sense to call setFuncObjFields in the case of string concatenation since the String object
//in the JPF heap has only three fields. This call will fail for any concatenation with more than three variables.
if (bmi.getBmType() != BootstrapMethodInfo.BMType.STRING_CONCATENATION) {
setFuncObjFields(ei, bmi, freeVariableTypeNames, freeVariableValues);
}

Perhaps we need to check the type of object that is created?

Yes, this is a good idea! We could use freeVarTypeNames argument of setFuncObjFields() to check that the type of an object argument is correct.

Okay, I'm working on it.