Skip to content

Commit 064801b

Browse files
authored
Model jdk.internal.misc.Unsafe to obtain sound results for ConcurrentHashMap (#201)
1 parent 5378767 commit 064801b

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

src/main/java/pascal/taie/analysis/pta/plugin/natives/UnsafeModel.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import pascal.taie.ir.exp.InstanceFieldAccess;
3030
import pascal.taie.ir.exp.Var;
3131
import pascal.taie.ir.stmt.Invoke;
32+
import pascal.taie.ir.stmt.LoadArray;
33+
import pascal.taie.ir.stmt.LoadField;
3234
import pascal.taie.ir.stmt.Stmt;
3335
import pascal.taie.ir.stmt.StoreArray;
3436
import pascal.taie.ir.stmt.StoreField;
@@ -50,7 +52,10 @@ public class UnsafeModel extends IRModelPlugin {
5052
super(solver);
5153
}
5254

53-
@InvokeHandler(signature = "<sun.misc.Unsafe: boolean compareAndSwapObject(java.lang.Object,long,java.lang.Object,java.lang.Object)>")
55+
@InvokeHandler(signature = {
56+
"<sun.misc.Unsafe: boolean compareAndSwapObject(java.lang.Object,long,java.lang.Object,java.lang.Object)>",
57+
"<jdk.internal.misc.Unsafe: boolean compareAndSetReference(java.lang.Object,long,java.lang.Object,java.lang.Object)>"
58+
})
5459
public List<Stmt> compareAndSwapObject(Invoke invoke) {
5560
// unsafe.compareAndSwapObject(o, offset, expected, x);
5661
List<Var> args = invoke.getInvokeExp().getArgs();
@@ -77,4 +82,59 @@ public List<Stmt> compareAndSwapObject(Invoke invoke) {
7782
}
7883
return stmts;
7984
}
85+
86+
@InvokeHandler(signature = "<jdk.internal.misc.Unsafe: java.lang.Object getReferenceAcquire(java.lang.Object,long)>")
87+
public List<Stmt> getReferenceAcquire(Invoke invoke) {
88+
// r = unsafe.getReferenceAcquire(o, offset);
89+
List<Var> args = invoke.getInvokeExp().getArgs();
90+
List<Stmt> stmts = new ArrayList<>();
91+
Var o = args.get(0);
92+
Var r = invoke.getResult();
93+
if (r == null) {
94+
return List.of();
95+
}
96+
if (o.getType() instanceof ArrayType) { // if o is of ArrayType
97+
// generate r = o[i];
98+
Var i = new Var(invoke.getContainer(),
99+
"%unsafe-index" + counter++, IntType.INT, -1);
100+
stmts.add(new LoadArray(r, new ArrayAccess(o, i)));
101+
} else { // otherwise, o is of ClassType
102+
// generate r = o.f; for every field f.
103+
JClass clazz = ((ClassType) o.getType()).getJClass();
104+
Type rType = r.getType();
105+
clazz.getDeclaredFields()
106+
.stream()
107+
.filter(field -> !field.isStatic())
108+
.filter(f -> f.getType().equals(rType))
109+
.forEach(f -> stmts.add(new LoadField(
110+
r, new InstanceFieldAccess(f.getRef(), o))));
111+
}
112+
return stmts;
113+
}
114+
115+
@InvokeHandler(signature = "<jdk.internal.misc.Unsafe: void putReferenceRelease(java.lang.Object,long,java.lang.Object)>")
116+
public List<Stmt> putReferenceRelease(Invoke invoke) {
117+
// unsafe.putReferenceRelease(o, offset, x);
118+
List<Var> args = invoke.getInvokeExp().getArgs();
119+
List<Stmt> stmts = new ArrayList<>();
120+
Var o = args.get(0);
121+
Var x = args.get(2);
122+
if (o.getType() instanceof ArrayType) { // if o is of ArrayType
123+
// generate o[i] = x;
124+
Var i = new Var(invoke.getContainer(),
125+
"%unsafe-index" + counter++, IntType.INT, -1);
126+
stmts.add(new StoreArray(new ArrayAccess(o, i), x));
127+
} else { // otherwise, o is of ClassType
128+
// generate o.f = x; for field f that has the same type of x.
129+
JClass clazz = ((ClassType) o.getType()).getJClass();
130+
Type xType = x.getType();
131+
clazz.getDeclaredFields()
132+
.stream()
133+
.filter(field -> !field.isStatic())
134+
.filter(f -> f.getType().equals(xType))
135+
.forEach(f -> stmts.add(new StoreField(
136+
new InstanceFieldAccess(f.getRef(), o), x)));
137+
}
138+
return stmts;
139+
}
80140
}

src/test/java/pascal/taie/analysis/pta/UnsafeModelTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ void testNonExistStaticField() {
3636
assertTrue(result.getInstanceFields().stream()
3737
.noneMatch(x -> x.getField().isStatic()));
3838
}
39+
40+
@Test
41+
void testUnsoundMap() {
42+
Tests.testPTA("misc", "UnsoundMap",
43+
"only-app:false", "dump:false", "expected-file:null");
44+
}
3945
}
1.05 KB
Binary file not shown.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import java.util.concurrent.ConcurrentHashMap;
2+
3+
public class UnsoundMap {
4+
public static void main(String[] args) {
5+
ConcurrentHashMap<Object, Object> map = new ConcurrentHashMap<>();
6+
String s = new String("514");
7+
map.put(114, s);
8+
PTAAssert.contains(map.get(114), s);
9+
map.clear();
10+
}
11+
}

0 commit comments

Comments
 (0)