From ff3fb8c932dc504feb0c7098632b1d8d1b13ce68 Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <2507097782@qq.com> Date: Wed, 13 Aug 2025 15:49:59 +0800 Subject: [PATCH 1/7] Improve Arrays.copyOf handling and add corresponding testcase --- .../{ArrayModel.java => ArrayCopyModel.java} | 13 +--- .../pta/plugin/natives/ArrayCopyOfModel.java | 67 +++++++++++++++++++ .../pta/plugin/natives/NativeModeller.java | 3 +- .../pascal/taie/analysis/pta/TaintTest.java | 2 + ...ngthArrayWithArraysCopyOf-pta-expected.txt | 2 + .../ZeroLengthArrayWithArraysCopyOf.java | 29 ++++++++ ...-zero-length-array-with-arrays-copy-of.yml | 6 ++ 7 files changed, 110 insertions(+), 12 deletions(-) rename src/main/java/pascal/taie/analysis/pta/plugin/natives/{ArrayModel.java => ArrayCopyModel.java} (86%) create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java create mode 100644 src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt create mode 100644 src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java create mode 100644 src/test/resources/pta/taint/taint-config-zero-length-array-with-arrays-copy-of.yml diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java similarity index 86% rename from src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java rename to src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java index 464c71bad..b00386ef2 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java @@ -29,7 +29,6 @@ import pascal.taie.ir.exp.CastExp; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Cast; -import pascal.taie.ir.stmt.Copy; import pascal.taie.ir.stmt.Invoke; import pascal.taie.ir.stmt.LoadArray; import pascal.taie.ir.stmt.Stmt; @@ -42,7 +41,7 @@ import java.util.List; -public class ArrayModel extends IRModelPlugin { +public class ArrayCopyModel extends IRModelPlugin { private final ClassType objType; @@ -53,20 +52,12 @@ public class ArrayModel extends IRModelPlugin { */ private int counter = 0; - ArrayModel(Solver solver) { + ArrayCopyModel(Solver solver) { super(solver); objType = typeSystem.getClassType(ClassNames.OBJECT); objArrayType = typeSystem.getArrayType(objType, 1); } - @InvokeHandler(signature = "") - public List arraysCopyOf(Invoke invoke) { - Var result = invoke.getResult(); - return result != null - ? List.of(new Copy(result, invoke.getInvokeExp().getArg(0))) - : List.of(); - } - @InvokeHandler(signature = "") public List systemArraycopy(Invoke invoke) { JMethod container = invoke.getContainer(); diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java new file mode 100644 index 000000000..5f5eb1c2d --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java @@ -0,0 +1,67 @@ +/* + * Tai-e: A Static Analysis Framework for Java + * + * Copyright (C) 2022 Tian Tan + * Copyright (C) 2022 Yue Li + * + * This file is part of Tai-e. + * + * Tai-e 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 3 + * of the License, or (at your option) any later version. + * + * Tai-e 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 Tai-e. If not, see . + */ + +package pascal.taie.analysis.pta.plugin.natives; + +import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.CSObj; +import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.analysis.pta.core.heap.Descriptor; +import pascal.taie.analysis.pta.core.heap.MockObj; +import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.util.AnalysisModelPlugin; +import pascal.taie.analysis.pta.plugin.util.InvokeHandler; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.util.collection.Sets; + +import java.util.Set; + +public class ArrayCopyOfModel extends AnalysisModelPlugin { + + private static final Descriptor generatedArrayDesc = () -> "ArrayGeneratedFromZeroLengthArray"; + + ArrayCopyOfModel(Solver solver) { + super(solver); + } + + @InvokeHandler(signature = "", argIndexes = {0}) + public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { + Var result = invoke.getResult(); + if(result != null){ + Pointer to = solver.getCSManager().getCSVar(context, result); + Set toObjs = Sets.newHybridSet(); + from.getObjects().forEach(csObj -> { + // handle the argument0's obj is zero-length-array obj + if(csObj.getObject() instanceof MockObj mockObj && mockObj.getDescriptor().string().equals("ZeroLengthArray")){ + toObjs.add(solver.getCSManager().getCSObj(context, solver.getHeapModel().getMockObj(generatedArrayDesc, invoke, mockObj.getType()))); + } + else { + toObjs.add(csObj); + } + }); + toObjs.forEach(csObj -> solver.addPointsTo(to, csObj)); + } + } + +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java index bec33238c..160d59926 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java @@ -33,7 +33,8 @@ public class NativeModeller extends CompositePlugin { @Override public void setSolver(Solver solver) { - addPlugin(new ArrayModel(solver), + addPlugin(new ArrayCopyModel(solver), + new ArrayCopyOfModel(solver), new UnsafeModel(solver), new DoPriviledgedModel(solver)); } diff --git a/src/test/java/pascal/taie/analysis/pta/TaintTest.java b/src/test/java/pascal/taie/analysis/pta/TaintTest.java index 86d082e2c..156708f01 100644 --- a/src/test/java/pascal/taie/analysis/pta/TaintTest.java +++ b/src/test/java/pascal/taie/analysis/pta/TaintTest.java @@ -74,6 +74,8 @@ public class TaintTest { TAINT_CONFIG_PREFIX + "taint-config-call-source.yml"}) @MultiStringsSource({"CallSiteMode", TAINT_CONFIG_PREFIX + "taint-config-call-site-model.yml"}) + @MultiStringsSource({"ZeroLengthArrayWithArraysCopyOf", + TAINT_CONFIG_PREFIX + "taint-config-zero-length-array-with-arrays-copy-of.yml"}) void test(String mainClass, String... opts) { testInNonInteractiveMode(mainClass, opts); testInInteractiveMode(mainClass, opts); diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt new file mode 100644 index 000000000..e61437518 --- /dev/null +++ b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt @@ -0,0 +1,2 @@ +Detected 1 taint flow(s): +TaintFlow{[10@L15] temp$8 = invokestatic ZeroLengthArrayWithArraysCopyOf.getSourceData()/result -> [15@L17] invokestatic ZeroLengthArrayWithArraysCopyOf.sink(temp$13)/0} diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java new file mode 100644 index 000000000..80b1e5ce2 --- /dev/null +++ b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java @@ -0,0 +1,29 @@ +import java.util.Arrays; + +public class ZeroLengthArrayWithArraysCopyOf{ + + public static void main(String[] args) { + testZeroLengthArrayPath(); + } + + public static void testZeroLengthArrayPath() { + + String[] original = new String[0]; + + String[] copy = Arrays.copyOf(original, original.length + 1); + + copy[copy.length - 1] = getSourceData(); + + sink(copy[copy.length - 1]); + + } + + public static String getSourceData() { + return "source_data"; + } + + public static void sink(String data) { + System.out.println("Sink: " + data); + } + +} diff --git a/src/test/resources/pta/taint/taint-config-zero-length-array-with-arrays-copy-of.yml b/src/test/resources/pta/taint/taint-config-zero-length-array-with-arrays-copy-of.yml new file mode 100644 index 000000000..b085b21d3 --- /dev/null +++ b/src/test/resources/pta/taint/taint-config-zero-length-array-with-arrays-copy-of.yml @@ -0,0 +1,6 @@ +sources: + - { kind: call, method: "", index: result } + +sinks: + - { method: "", index: 0 } + From e5717bd7d6e10fed90e1d96d62990e97fb68d077 Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <2507097782@qq.com> Date: Thu, 14 Aug 2025 21:40:09 +0800 Subject: [PATCH 2/7] Address review comments --- .../taie/analysis/pta/core/heap/AbstractHeapModel.java | 2 +- .../analysis/pta/plugin/natives/ArrayCopyOfModel.java | 9 ++++++--- .../ZeroLengthArrayWithArraysCopyOf-pta-expected.txt | 2 +- .../pta/taint/ZeroLengthArrayWithArraysCopyOf.java | 5 ----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java index d6a46dad8..4f4d7b189 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java +++ b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java @@ -97,7 +97,7 @@ public abstract class AbstractHeapModel implements HeapModel { private final Map zeroLengthArrays = Maps.newMap(); - private static final Descriptor zeroLengthArrayDesc = () -> "ZeroLengthArray"; + public static final Descriptor zeroLengthArrayDesc = () -> "ZeroLengthArray"; /** * Counter for indexing Objs. diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java index 5f5eb1c2d..061b4b7b3 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java @@ -25,6 +25,7 @@ import pascal.taie.analysis.pta.core.cs.context.Context; import pascal.taie.analysis.pta.core.cs.element.CSObj; import pascal.taie.analysis.pta.core.cs.element.Pointer; +import pascal.taie.analysis.pta.core.heap.AbstractHeapModel; import pascal.taie.analysis.pta.core.heap.Descriptor; import pascal.taie.analysis.pta.core.heap.MockObj; import pascal.taie.analysis.pta.core.solver.Solver; @@ -39,7 +40,7 @@ public class ArrayCopyOfModel extends AnalysisModelPlugin { - private static final Descriptor generatedArrayDesc = () -> "ArrayGeneratedFromZeroLengthArray"; + private static final Descriptor COPYOF_ARRAY_DESC = () -> "ArrayGeneratedByCopyOfModel"; ArrayCopyOfModel(Solver solver) { super(solver); @@ -53,8 +54,10 @@ public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { Set toObjs = Sets.newHybridSet(); from.getObjects().forEach(csObj -> { // handle the argument0's obj is zero-length-array obj - if(csObj.getObject() instanceof MockObj mockObj && mockObj.getDescriptor().string().equals("ZeroLengthArray")){ - toObjs.add(solver.getCSManager().getCSObj(context, solver.getHeapModel().getMockObj(generatedArrayDesc, invoke, mockObj.getType()))); + if(csObj.getObject() instanceof MockObj mockObj + && mockObj.getDescriptor().equals(AbstractHeapModel.zeroLengthArrayDesc)){ + toObjs.add(solver.getCSManager().getCSObj(context, + heapModel.getMockObj(COPYOF_ARRAY_DESC, invoke, mockObj.getType()))); } else { toObjs.add(csObj); diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt index e61437518..6291c5fce 100644 --- a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt +++ b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt @@ -1,2 +1,2 @@ Detected 1 taint flow(s): -TaintFlow{[10@L15] temp$8 = invokestatic ZeroLengthArrayWithArraysCopyOf.getSourceData()/result -> [15@L17] invokestatic ZeroLengthArrayWithArraysCopyOf.sink(temp$13)/0} +TaintFlow{[10@L12] temp$8 = invokestatic ZeroLengthArrayWithArraysCopyOf.getSourceData()/result -> [15@L13] invokestatic ZeroLengthArrayWithArraysCopyOf.sink(temp$13)/0} diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java index 80b1e5ce2..195a3c5fb 100644 --- a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java +++ b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java @@ -7,15 +7,10 @@ public static void main(String[] args) { } public static void testZeroLengthArrayPath() { - String[] original = new String[0]; - String[] copy = Arrays.copyOf(original, original.length + 1); - copy[copy.length - 1] = getSourceData(); - sink(copy[copy.length - 1]); - } public static String getSourceData() { From 0bda0e82a58bc13113f0be963e45664c0ead726b Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <2507097782@qq.com> Date: Thu, 14 Aug 2025 21:43:54 +0800 Subject: [PATCH 3/7] capitalize zeroLengthArrayDesc --- .../pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java | 4 ++-- .../taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java index 4f4d7b189..f05fe93a2 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java +++ b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java @@ -97,7 +97,7 @@ public abstract class AbstractHeapModel implements HeapModel { private final Map zeroLengthArrays = Maps.newMap(); - public static final Descriptor zeroLengthArrayDesc = () -> "ZeroLengthArray"; + public static final Descriptor ZERO_LENGTH_ARRAY_DESC = () -> "ZeroLengthArray"; /** * Counter for indexing Objs. @@ -204,7 +204,7 @@ protected NewObj getNewObj(New allocSite) { */ protected Obj getZeroLengthArrayObj(Type type) { return zeroLengthArrays.computeIfAbsent(type, - t -> getMockObj(zeroLengthArrayDesc, + t -> getMockObj(ZERO_LENGTH_ARRAY_DESC, "", t, false)); } diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java index 061b4b7b3..82cf6be68 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java @@ -55,7 +55,7 @@ public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { from.getObjects().forEach(csObj -> { // handle the argument0's obj is zero-length-array obj if(csObj.getObject() instanceof MockObj mockObj - && mockObj.getDescriptor().equals(AbstractHeapModel.zeroLengthArrayDesc)){ + && mockObj.getDescriptor().equals(AbstractHeapModel.ZERO_LENGTH_ARRAY_DESC)){ toObjs.add(solver.getCSManager().getCSObj(context, heapModel.getMockObj(COPYOF_ARRAY_DESC, invoke, mockObj.getType()))); } From 48b984aba2f3f28acb0ebd38d9f2dfb3553c4c56 Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <2507097782@qq.com> Date: Wed, 3 Sep 2025 22:14:50 +0800 Subject: [PATCH 4/7] Address review comments --- .../pta/plugin/natives/ArrayCopyOfModel.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java index 82cf6be68..99e1dec18 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java @@ -23,20 +23,15 @@ package pascal.taie.analysis.pta.plugin.natives; import pascal.taie.analysis.pta.core.cs.context.Context; -import pascal.taie.analysis.pta.core.cs.element.CSObj; -import pascal.taie.analysis.pta.core.cs.element.Pointer; import pascal.taie.analysis.pta.core.heap.AbstractHeapModel; import pascal.taie.analysis.pta.core.heap.Descriptor; -import pascal.taie.analysis.pta.core.heap.MockObj; import pascal.taie.analysis.pta.core.solver.Solver; import pascal.taie.analysis.pta.plugin.util.AnalysisModelPlugin; +import pascal.taie.analysis.pta.plugin.util.CSObjs; import pascal.taie.analysis.pta.plugin.util.InvokeHandler; import pascal.taie.analysis.pta.pts.PointsToSet; import pascal.taie.ir.exp.Var; import pascal.taie.ir.stmt.Invoke; -import pascal.taie.util.collection.Sets; - -import java.util.Set; public class ArrayCopyOfModel extends AnalysisModelPlugin { @@ -49,21 +44,16 @@ public class ArrayCopyOfModel extends AnalysisModelPlugin { @InvokeHandler(signature = "", argIndexes = {0}) public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { Var result = invoke.getResult(); - if(result != null){ - Pointer to = solver.getCSManager().getCSVar(context, result); - Set toObjs = Sets.newHybridSet(); + if (result != null) { from.getObjects().forEach(csObj -> { - // handle the argument0's obj is zero-length-array obj - if(csObj.getObject() instanceof MockObj mockObj - && mockObj.getDescriptor().equals(AbstractHeapModel.ZERO_LENGTH_ARRAY_DESC)){ - toObjs.add(solver.getCSManager().getCSObj(context, - heapModel.getMockObj(COPYOF_ARRAY_DESC, invoke, mockObj.getType()))); - } - else { - toObjs.add(csObj); + // handle the argument0's obj contains zero-length-array obj + if (CSObjs.hasDescriptor(csObj, AbstractHeapModel.ZERO_LENGTH_ARRAY_DESC)) { + solver.addVarPointsTo(context, result, csManager.getCSObj(context, + heapModel.getMockObj(COPYOF_ARRAY_DESC, invoke, csObj.getObject().getType()))); + } else { + solver.addVarPointsTo(context, result, csObj); } }); - toObjs.forEach(csObj -> solver.addPointsTo(to, csObj)); } } From 86ef02d385db31febde70e20115d9dae06f16a38 Mon Sep 17 00:00:00 2001 From: jpwang Date: Fri, 5 Sep 2025 18:57:41 +0800 Subject: [PATCH 5/7] Refactor --- .../pta/core/heap/AbstractHeapModel.java | 2 +- .../pta/plugin/natives/ArrayCopyModel.java | 79 ------------ .../pta/plugin/natives/ArrayCopyOfModel.java | 60 --------- .../pta/plugin/natives/ArrayModel.java | 117 ++++++++++++++++++ .../pta/plugin/natives/NativeModeller.java | 4 +- .../ZeroLengthArrayWithArraysCopyOf.java | 2 +- 6 files changed, 121 insertions(+), 143 deletions(-) delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java delete mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java create mode 100644 src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java diff --git a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java index f05fe93a2..187312dad 100644 --- a/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java +++ b/src/main/java/pascal/taie/analysis/pta/core/heap/AbstractHeapModel.java @@ -97,7 +97,7 @@ public abstract class AbstractHeapModel implements HeapModel { private final Map zeroLengthArrays = Maps.newMap(); - public static final Descriptor ZERO_LENGTH_ARRAY_DESC = () -> "ZeroLengthArray"; + private static final Descriptor ZERO_LENGTH_ARRAY_DESC = () -> "ZeroLengthArray"; /** * Counter for indexing Objs. diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java deleted file mode 100644 index b00386ef2..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyModel.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Tai-e: A Static Analysis Framework for Java - * - * Copyright (C) 2022 Tian Tan - * Copyright (C) 2022 Yue Li - * - * This file is part of Tai-e. - * - * Tai-e 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 3 - * of the License, or (at your option) any later version. - * - * Tai-e 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 Tai-e. If not, see . - */ - -package pascal.taie.analysis.pta.plugin.natives; - -import pascal.taie.analysis.pta.core.solver.Solver; -import pascal.taie.analysis.pta.plugin.util.IRModelPlugin; -import pascal.taie.analysis.pta.plugin.util.InvokeHandler; -import pascal.taie.ir.exp.ArrayAccess; -import pascal.taie.ir.exp.CastExp; -import pascal.taie.ir.exp.Var; -import pascal.taie.ir.stmt.Cast; -import pascal.taie.ir.stmt.Invoke; -import pascal.taie.ir.stmt.LoadArray; -import pascal.taie.ir.stmt.Stmt; -import pascal.taie.ir.stmt.StoreArray; -import pascal.taie.language.classes.ClassNames; -import pascal.taie.language.classes.JMethod; -import pascal.taie.language.type.ArrayType; -import pascal.taie.language.type.ClassType; -import pascal.taie.language.type.Type; - -import java.util.List; - -public class ArrayCopyModel extends IRModelPlugin { - - private final ClassType objType; - - private final ArrayType objArrayType; - - /** - * Counter for naming temporary variables. - */ - private int counter = 0; - - ArrayCopyModel(Solver solver) { - super(solver); - objType = typeSystem.getClassType(ClassNames.OBJECT); - objArrayType = typeSystem.getArrayType(objType, 1); - } - - @InvokeHandler(signature = "") - public List systemArraycopy(Invoke invoke) { - JMethod container = invoke.getContainer(); - Var src = getTempVar(container, "src", objArrayType); - Var dest = getTempVar(container, "dest", objArrayType); - Var temp = getTempVar(container, "temp", objType); - List args = invoke.getInvokeExp().getArgs(); - return List.of( - new Cast(src, new CastExp(args.get(0), objArrayType)), - new Cast(dest, new CastExp(args.get(2), objArrayType)), - new LoadArray(temp, new ArrayAccess(src, args.get(1))), - new StoreArray(new ArrayAccess(dest, args.get(3)), temp)); - } - - private Var getTempVar(JMethod container, String name, Type type) { - String varName = "%native-arraycopy-" + name + counter++; - return new Var(container, varName, type, -1); - } -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java deleted file mode 100644 index 99e1dec18..000000000 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayCopyOfModel.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Tai-e: A Static Analysis Framework for Java - * - * Copyright (C) 2022 Tian Tan - * Copyright (C) 2022 Yue Li - * - * This file is part of Tai-e. - * - * Tai-e 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 3 - * of the License, or (at your option) any later version. - * - * Tai-e 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 Tai-e. If not, see . - */ - -package pascal.taie.analysis.pta.plugin.natives; - -import pascal.taie.analysis.pta.core.cs.context.Context; -import pascal.taie.analysis.pta.core.heap.AbstractHeapModel; -import pascal.taie.analysis.pta.core.heap.Descriptor; -import pascal.taie.analysis.pta.core.solver.Solver; -import pascal.taie.analysis.pta.plugin.util.AnalysisModelPlugin; -import pascal.taie.analysis.pta.plugin.util.CSObjs; -import pascal.taie.analysis.pta.plugin.util.InvokeHandler; -import pascal.taie.analysis.pta.pts.PointsToSet; -import pascal.taie.ir.exp.Var; -import pascal.taie.ir.stmt.Invoke; - -public class ArrayCopyOfModel extends AnalysisModelPlugin { - - private static final Descriptor COPYOF_ARRAY_DESC = () -> "ArrayGeneratedByCopyOfModel"; - - ArrayCopyOfModel(Solver solver) { - super(solver); - } - - @InvokeHandler(signature = "", argIndexes = {0}) - public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { - Var result = invoke.getResult(); - if (result != null) { - from.getObjects().forEach(csObj -> { - // handle the argument0's obj contains zero-length-array obj - if (CSObjs.hasDescriptor(csObj, AbstractHeapModel.ZERO_LENGTH_ARRAY_DESC)) { - solver.addVarPointsTo(context, result, csManager.getCSObj(context, - heapModel.getMockObj(COPYOF_ARRAY_DESC, invoke, csObj.getObject().getType()))); - } else { - solver.addVarPointsTo(context, result, csObj); - } - }); - } - } - -} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java new file mode 100644 index 000000000..b16c43be6 --- /dev/null +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java @@ -0,0 +1,117 @@ +/* + * Tai-e: A Static Analysis Framework for Java + * + * Copyright (C) 2022 Tian Tan + * Copyright (C) 2022 Yue Li + * + * This file is part of Tai-e. + * + * Tai-e 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 3 + * of the License, or (at your option) any later version. + * + * Tai-e 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 Tai-e. If not, see . + */ + +package pascal.taie.analysis.pta.plugin.natives; + +import pascal.taie.analysis.pta.core.cs.context.Context; +import pascal.taie.analysis.pta.core.cs.element.CSObj; +import pascal.taie.analysis.pta.core.heap.Descriptor; +import pascal.taie.analysis.pta.core.heap.Obj; +import pascal.taie.analysis.pta.core.solver.Solver; +import pascal.taie.analysis.pta.plugin.util.AnalysisModelPlugin; +import pascal.taie.analysis.pta.plugin.util.IRModelPlugin; +import pascal.taie.analysis.pta.plugin.util.InvokeHandler; +import pascal.taie.analysis.pta.pts.PointsToSet; +import pascal.taie.ir.exp.ArrayAccess; +import pascal.taie.ir.exp.CastExp; +import pascal.taie.ir.exp.Var; +import pascal.taie.ir.stmt.Cast; +import pascal.taie.ir.stmt.Invoke; +import pascal.taie.ir.stmt.LoadArray; +import pascal.taie.ir.stmt.Stmt; +import pascal.taie.ir.stmt.StoreArray; +import pascal.taie.language.classes.ClassNames; +import pascal.taie.language.classes.JMethod; +import pascal.taie.language.type.ArrayType; +import pascal.taie.language.type.ClassType; +import pascal.taie.language.type.Type; + +import java.util.List; + +public class ArrayModel { + + private static final Descriptor COPY_OF_ARRAY_DESC = () -> "ArrayGeneratedByCopyOfModel"; + + public static class AnalysisModel extends AnalysisModelPlugin { + + AnalysisModel(Solver solver) { + super(solver); + } + + @InvokeHandler(signature = "", argIndexes = {0}) + public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { + JMethod container = invoke.getContainer(); + Var result = invoke.getResult(); + if (result != null) { + from.getObjects().forEach(csObj -> { + // When the array object from the first argument is not functional, + // create a new functional array + if (!csObj.getObject().isFunctional()) { + Type type = csObj.getObject().getType(); + Obj newArray = heapModel.getMockObj(COPY_OF_ARRAY_DESC, invoke, type, container); + CSObj csNewArray = csManager.getCSObj(context, newArray); + solver.addVarPointsTo(context, result, csNewArray); + } else { + solver.addVarPointsTo(context, result, csObj); + } + }); + } + } + } + + public static class IRModel extends IRModelPlugin { + + private final ClassType objType; + + private final ArrayType objArrayType; + + /** + * Counter for naming temporary variables. + */ + private int counter = 0; + + IRModel(Solver solver) { + super(solver); + objType = typeSystem.getClassType(ClassNames.OBJECT); + objArrayType = typeSystem.getArrayType(objType, 1); + } + + @InvokeHandler(signature = "") + public List systemArraycopy(Invoke invoke) { + JMethod container = invoke.getContainer(); + Var src = getTempVar(container, "src", objArrayType); + Var dest = getTempVar(container, "dest", objArrayType); + Var temp = getTempVar(container, "temp", objType); + List args = invoke.getInvokeExp().getArgs(); + return List.of( + new Cast(src, new CastExp(args.get(0), objArrayType)), + new Cast(dest, new CastExp(args.get(2), objArrayType)), + new LoadArray(temp, new ArrayAccess(src, args.get(1))), + new StoreArray(new ArrayAccess(dest, args.get(3)), temp)); + } + + private Var getTempVar(JMethod container, String name, Type type) { + String varName = "%native-arraycopy-" + name + counter++; + return new Var(container, varName, type, -1); + } + } +} diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java index 160d59926..13b20248b 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/NativeModeller.java @@ -33,8 +33,8 @@ public class NativeModeller extends CompositePlugin { @Override public void setSolver(Solver solver) { - addPlugin(new ArrayCopyModel(solver), - new ArrayCopyOfModel(solver), + addPlugin(new ArrayModel.AnalysisModel(solver), + new ArrayModel.IRModel(solver), new UnsafeModel(solver), new DoPriviledgedModel(solver)); } diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java index 195a3c5fb..d4f48030c 100644 --- a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java +++ b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java @@ -1,6 +1,6 @@ import java.util.Arrays; -public class ZeroLengthArrayWithArraysCopyOf{ +public class ZeroLengthArrayWithArraysCopyOf { public static void main(String[] args) { testZeroLengthArrayPath(); From 39da71ee820349e1fd5c29d90cbd2c1c3ac62a4c Mon Sep 17 00:00:00 2001 From: jpwang Date: Wed, 10 Sep 2025 16:14:54 +0800 Subject: [PATCH 6/7] Ignore `Arrays.copyOf()` for PTA Solver to address precision degradation --- .../taie/analysis/pta/plugin/natives/ArrayModel.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java index b16c43be6..f7378e3d0 100644 --- a/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java +++ b/src/main/java/pascal/taie/analysis/pta/plugin/natives/ArrayModel.java @@ -57,6 +57,13 @@ public static class AnalysisModel extends AnalysisModelPlugin { super(solver); } + @Override + public void onStart() { + // Solver should ignore `Arrays.copyOf()` to avoid spurious flows merging from other + // callsites, as in the `IRModelPlugin.onStart()`. + handlers.keySet().forEach(solver::addIgnoredMethod); + } + @InvokeHandler(signature = "", argIndexes = {0}) public void arraysCopyOf(Context context, Invoke invoke, PointsToSet from) { JMethod container = invoke.getContainer(); From 9749d849c3e049021824d0f1b47daf33d2180534 Mon Sep 17 00:00:00 2001 From: Zhiwei Zhang <2507097782@qq.com> Date: Wed, 10 Sep 2025 20:49:11 +0800 Subject: [PATCH 7/7] Modified the test file to use PTAAssert instead of expected-file --- .../pascal/taie/analysis/pta/BasicTestFull.java | 16 ++++++++++++++++ .../java/pascal/taie/analysis/pta/TaintTest.java | 2 -- .../ZeroLengthArrayWithArraysCopyOf.java | 12 ++++++------ ...oLengthArrayWithArraysCopyOf-pta-expected.txt | 2 -- 4 files changed, 22 insertions(+), 10 deletions(-) rename src/test/resources/pta/{taint => basic}/ZeroLengthArrayWithArraysCopyOf.java (63%) delete mode 100644 src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt diff --git a/src/test/java/pascal/taie/analysis/pta/BasicTestFull.java b/src/test/java/pascal/taie/analysis/pta/BasicTestFull.java index fe5ba2a61..330cb8a5a 100644 --- a/src/test/java/pascal/taie/analysis/pta/BasicTestFull.java +++ b/src/test/java/pascal/taie/analysis/pta/BasicTestFull.java @@ -22,8 +22,11 @@ package pascal.taie.analysis.pta; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import pascal.taie.Main; import pascal.taie.analysis.Tests; +import pascal.taie.analysis.pta.plugin.assertion.AssertionChecker; import pascal.taie.util.MultiStringsSource; public class BasicTestFull extends BasicTest { @@ -55,4 +58,17 @@ void testFull(String mainClass, String... opts) { Tests.testPTA(DIR, mainClass, opts); } + @Test + void testZeroLengthArrayWithArraysCopyOf() { + String ptaTestRoot = "src/test/resources/pta"; + String classPath = ptaTestRoot + "/" + DIR; + Main.main("-pp", + "-cp", ptaTestRoot, + "-cp", classPath, + "-m", "ZeroLengthArrayWithArraysCopyOf", + "-a", "pta=implicit-entries:false;" + + "plugins:[" + AssertionChecker.class.getName() + "]" + ); + } + } diff --git a/src/test/java/pascal/taie/analysis/pta/TaintTest.java b/src/test/java/pascal/taie/analysis/pta/TaintTest.java index 156708f01..86d082e2c 100644 --- a/src/test/java/pascal/taie/analysis/pta/TaintTest.java +++ b/src/test/java/pascal/taie/analysis/pta/TaintTest.java @@ -74,8 +74,6 @@ public class TaintTest { TAINT_CONFIG_PREFIX + "taint-config-call-source.yml"}) @MultiStringsSource({"CallSiteMode", TAINT_CONFIG_PREFIX + "taint-config-call-site-model.yml"}) - @MultiStringsSource({"ZeroLengthArrayWithArraysCopyOf", - TAINT_CONFIG_PREFIX + "taint-config-zero-length-array-with-arrays-copy-of.yml"}) void test(String mainClass, String... opts) { testInNonInteractiveMode(mainClass, opts); testInInteractiveMode(mainClass, opts); diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java b/src/test/resources/pta/basic/ZeroLengthArrayWithArraysCopyOf.java similarity index 63% rename from src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java rename to src/test/resources/pta/basic/ZeroLengthArrayWithArraysCopyOf.java index d4f48030c..23934a4bb 100644 --- a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf.java +++ b/src/test/resources/pta/basic/ZeroLengthArrayWithArraysCopyOf.java @@ -9,16 +9,16 @@ public static void main(String[] args) { public static void testZeroLengthArrayPath() { String[] original = new String[0]; String[] copy = Arrays.copyOf(original, original.length + 1); - copy[copy.length - 1] = getSourceData(); - sink(copy[copy.length - 1]); + PTAAssert.sizeEquals(1, copy); + PTAAssert.notEquals(original, copy); + String src = getSourceData(); + copy[copy.length - 1] = src; + String dst = copy[copy.length - 1]; + PTAAssert.equals(dst, src); } public static String getSourceData() { return "source_data"; } - public static void sink(String data) { - System.out.println("Sink: " + data); - } - } diff --git a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt b/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt deleted file mode 100644 index 6291c5fce..000000000 --- a/src/test/resources/pta/taint/ZeroLengthArrayWithArraysCopyOf-pta-expected.txt +++ /dev/null @@ -1,2 +0,0 @@ -Detected 1 taint flow(s): -TaintFlow{[10@L12] temp$8 = invokestatic ZeroLengthArrayWithArraysCopyOf.getSourceData()/result -> [15@L13] invokestatic ZeroLengthArrayWithArraysCopyOf.sink(temp$13)/0}