From 5b72aee3ae65e8f91c54f5bdb7ea90a90eab60ac Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 6 Nov 2023 16:04:03 +0000 Subject: [PATCH 1/3] Java: model JDK21 SequencedCollection, Set and Map --- java/ql/lib/ext/java.util.model.yml | 28 ++++ .../library-tests/dataflow/collections/B.java | 141 ++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/java/ql/lib/ext/java.util.model.yml b/java/ql/lib/ext/java.util.model.yml index 5e1463930b7f..49ed1d5a355f 100644 --- a/java/ql/lib/ext/java.util.model.yml +++ b/java/ql/lib/ext/java.util.model.yml @@ -320,6 +320,34 @@ extensions: - ["java.util", "Scanner", True, "useDelimiter", "", "", "Argument[this]", "ReturnValue", "value", "manual"] - ["java.util", "Scanner", True, "useLocale", "", "", "Argument[this]", "ReturnValue", "value", "manual"] - ["java.util", "Scanner", True, "useRadix", "", "", "Argument[this]", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedCollection", True, "addFirst", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + - ["java.util", "SequencedCollection", True, "addLast", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + - ["java.util", "SequencedCollection", True, "getFirst", "", "", "Argument[this].Element", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedCollection", True, "getLast", "", "", "Argument[this].Element", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedCollection", True, "removeFirst", "", "", "Argument[this].Element", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedCollection", True, "removeLast", "", "", "Argument[this].Element", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedCollection", True, "reversed", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"] + - ["java.util", "SequencedMap", True, "firstEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "firstEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "lastEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "lastEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "pollFirstEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "pollFirstEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "pollLastEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "pollLastEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[this].MapValue", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[1]", "Argument[this].MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[this].MapValue", "ReturnValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[1]", "Argument[this].MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "reversed", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "reversed", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "sequencedEntrySet", "", "", "Argument[this].MapKey", "ReturnValue.Element.MapKey", "value", "manual"] + - ["java.util", "SequencedMap", True, "sequencedEntrySet", "", "", "Argument[this].MapValue", "ReturnValue.Element.MapValue", "value", "manual"] + - ["java.util", "SequencedMap", True, "sequencedKeySet", "", "", "Argument[this].MapKey", "ReturnValue.Element", "value", "manual"] + - ["java.util", "SequencedMap", True, "sequencedValues", "", "", "Argument[this].MapValue", "ReturnValue.Element", "value", "manual"] + - ["java.util", "SequencedSet", True, "reversed", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"] - ["java.util", "Set", False, "copyOf", "(Collection)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] - ["java.util", "Set", False, "clear", "()", "", "Argument[this].WithoutElement", "Argument[this]", "value", "manual"] - ["java.util", "Set", False, "of", "(Object)", "", "Argument[0]", "ReturnValue.Element", "value", "manual"] diff --git a/java/ql/test/library-tests/dataflow/collections/B.java b/java/ql/test/library-tests/dataflow/collections/B.java index 7d9c418c162b..af95891f719b 100644 --- a/java/ql/test/library-tests/dataflow/collections/B.java +++ b/java/ql/test/library-tests/dataflow/collections/B.java @@ -1873,5 +1873,146 @@ void foo() throws InterruptedException { Collection out = null; Object[] in = storeArrayElement(source()); ((Collections)null).addAll(out,in); sink(readElement(out)); // $ hasValueFlow } + // Java 21 sequenced collections tests: + { + // ["java.util", "SequencedCollection", True, "addFirst", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + SequencedCollection out = null; + Object in = source(); out.addFirst(in); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "addLast", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + SequencedCollection out = null; + Object in = source(); out.addLast(in); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "getFirst", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + Object out = null; + SequencedCollection in = storeElementList(source()); out = in.getFirst(); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "getLast", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + Object out = null; + SequencedCollection in = storeElementList(source()); out = in.getLast(); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "removeFirst", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + Object out = null; + SequencedCollection in = storeElementList(source()); out = in.removeFirst(); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "removeLast", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"] + Object out = null; + SequencedCollection in = storeElementList(source()); out = in.removeLast(); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedCollection", True, "reversed", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"] + SequencedCollection out = null; + SequencedCollection in = storeElementList(source()); out = in.reversed(); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "firstEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.firstEntry(); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "lastEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.lastEntry(); sink(readMapKey(out)); // $ hasValueFlow + } + { + // - ["java.util", "SequencedMap", True, "pollFirstEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.pollFirstEntry(); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "pollLastEntry", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.pollLastEntry(); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[this].MapValue", "ReturnValue", "value", "manual"] + Object out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.putFirst(null, null); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"] + SequencedMap out = null; + Object in = source(); out.putFirst(in, null); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[this].MapValue", "ReturnValue", "value", "manual"] + Object out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.putLast(null, null); sink(out); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"] + SequencedMap out = null; + Object in = source(); out.putLast(in, null); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "reversed", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"] + SequencedMap out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.reversed(); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "sequencedEntrySet", "", "", "Argument[this].MapKey", "ReturnValue.Element.MapKey", "value", "manual"] + Set out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.sequencedEntrySet(); sink(readMapKey(readElement(out))); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "firstEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.firstEntry(); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "lastEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.lastEntry(); sink(readMapValue(out)); // $ hasValueFlow + } + { + // - ["java.util", "SequencedMap", True, "pollFirstEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.pollFirstEntry(); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "pollLastEntry", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + Map.Entry out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.pollLastEntry(); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putFirst", "", "", "Argument[0]", "Argument[this].MapValue", "value", "manual"] + SequencedMap out = null; + Object in = source(); out.putFirst(null, in); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "putLast", "", "", "Argument[0]", "Argument[this].MapValue", "value", "manual"] + SequencedMap out = null; + Object in = source(); out.putLast(null, in); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "reversed", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"] + SequencedMap out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.reversed(); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "sequencedEntrySet", "", "", "Argument[this].MapValue", "ReturnValue.Element.MapValue", "value", "manual"] + Set out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.sequencedEntrySet(); sink(readMapValue(readElement(out))); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "sequencedKeySet", "", "", "Argument[this].MapKey", "ReturnValue.Element", "value", "manual"] + Set out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = in.sequencedKeySet(); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedMap", True, "sequencedValues", "", "", "Argument[this].MapValue", "ReturnValue.Element", "value", "manual"] + SequencedCollection out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = in.sequencedValues(); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "SequencedSet", True, "reversed", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"] + SequencedSet out = null; + SequencedSet in = storeElementNavSet(source()); out = in.reversed(); sink(readElement(out)); // $ hasValueFlow + } } } From d30d71e048ee1810e3e1c282e82240108452c06a Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 6 Nov 2023 16:38:44 +0000 Subject: [PATCH 2/3] Add change note --- java/ql/lib/change-notes/2023-11-06-jdk21-models.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2023-11-06-jdk21-models.md diff --git a/java/ql/lib/change-notes/2023-11-06-jdk21-models.md b/java/ql/lib/change-notes/2023-11-06-jdk21-models.md new file mode 100644 index 000000000000..ac3d6b1ff96d --- /dev/null +++ b/java/ql/lib/change-notes/2023-11-06-jdk21-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The types `java.util.SequencedCollection`, `SequencedSet` and `SequencedMap`, as well as the related `Collections.unmodifiableSequenced*` methods are now modelled. This means alerts may be raised relating to data flow through these types and methods. From 24b4b05be89213d62bc1c4067f69997692d65908 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 6 Nov 2023 16:44:40 +0000 Subject: [PATCH 3/3] Add models for new Collections methods --- java/ql/lib/ext/java.util.model.yml | 4 ++++ .../library-tests/dataflow/collections/B.java | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/java/ql/lib/ext/java.util.model.yml b/java/ql/lib/ext/java.util.model.yml index 49ed1d5a355f..07eae7c95b45 100644 --- a/java/ql/lib/ext/java.util.model.yml +++ b/java/ql/lib/ext/java.util.model.yml @@ -99,6 +99,10 @@ extensions: - ["java.util", "Collections", False, "unmodifiableNavigableMap", "(NavigableMap)", "", "Argument[0].MapKey", "ReturnValue.MapKey", "value", "manual"] - ["java.util", "Collections", False, "unmodifiableNavigableMap", "(NavigableMap)", "", "Argument[0].MapValue", "ReturnValue.MapValue", "value", "manual"] - ["java.util", "Collections", False, "unmodifiableNavigableSet", "(NavigableSet)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] + - ["java.util", "Collections", False, "unmodifiableSequencedCollection", "", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] + - ["java.util", "Collections", False, "unmodifiableSequencedMap", "", "", "Argument[0].MapKey", "ReturnValue.MapKey", "value", "manual"] + - ["java.util", "Collections", False, "unmodifiableSequencedMap", "", "", "Argument[0].MapValue", "ReturnValue.MapValue", "value", "manual"] + - ["java.util", "Collections", False, "unmodifiableSequencedSet", "", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] - ["java.util", "Collections", False, "unmodifiableSet", "(Set)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] - ["java.util", "Collections", False, "unmodifiableSortedMap", "(SortedMap)", "", "Argument[0].MapKey", "ReturnValue.MapKey", "value", "manual"] - ["java.util", "Collections", False, "unmodifiableSortedMap", "(SortedMap)", "", "Argument[0].MapValue", "ReturnValue.MapValue", "value", "manual"] diff --git a/java/ql/test/library-tests/dataflow/collections/B.java b/java/ql/test/library-tests/dataflow/collections/B.java index af95891f719b..540e1ac2623a 100644 --- a/java/ql/test/library-tests/dataflow/collections/B.java +++ b/java/ql/test/library-tests/dataflow/collections/B.java @@ -2014,5 +2014,25 @@ void foo() throws InterruptedException { SequencedSet out = null; SequencedSet in = storeElementNavSet(source()); out = in.reversed(); sink(readElement(out)); // $ hasValueFlow } + { + // ["java.util", "Collections", False, "unmodifiableSequencedCollection", "", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] + SequencedCollection out = null; + SequencedCollection in = storeElementList(source()); out = Collections.unmodifiableSequencedCollection(in); sink(readElement(out)); // $ hasValueFlow + } + { + // ["java.util", "Collections", False, "unmodifiableSequencedMap", "", "", "Argument[0].MapKey", "ReturnValue.MapKey", "value", "manual"] + SequencedMap out = null; + SequencedMap in = (SequencedMap)storeMapKey(source()); out = Collections.unmodifiableSequencedMap(in); sink(readMapKey(out)); // $ hasValueFlow + } + { + // ["java.util", "Collections", False, "unmodifiableSequencedMap", "", "", "Argument[0].MapValue", "ReturnValue.MapValue", "value", "manual"] + SequencedMap out = null; + SequencedMap in = (SequencedMap)storeMapValue(source()); out = Collections.unmodifiableSequencedMap(in); sink(readMapValue(out)); // $ hasValueFlow + } + { + // ["java.util", "Collections", False, "unmodifiableSequencedSet", "", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"] + SequencedSet out = null; + SequencedSet in = storeElementNavSet(source()); out = Collections.unmodifiableSequencedSet(in); sink(readElement(out)); // $ hasValueFlow + } } }