From bdcf4198e60b9957f0da04a204d13fa028e362e1 Mon Sep 17 00:00:00 2001
From: "lcartey@github.com"
Date: Tue, 30 Jun 2020 11:01:17 +0100
Subject: [PATCH 001/185] Add additional Hibernate SQL sinks
---
java/ql/src/semmle/code/java/frameworks/Hibernate.qll | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
index fdc237aac8a..1366b3cac45 100644
--- a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
@@ -10,11 +10,11 @@ class HibernateSession extends RefType {
}
/**
- * Holds if `m` is a method on `HibernateSession` taking an SQL string as its
- * first argument.
+ * Holds if `m` is a method on `HibernateSession`, or a subclass, taking an SQL
+ * string as its first argument.
*/
predicate hibernateSqlMethod(Method m) {
- m.getDeclaringType() instanceof HibernateSession and
+ m.getDeclaringType().getASourceSupertype*() instanceof HibernateSession and
m.getParameterType(0) instanceof TypeString and
(
m.hasName("createQuery") or
From 26639a113e3991e755464ce4089e67a6e34076dd Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Wed, 2 Sep 2020 19:41:22 +0200
Subject: [PATCH 002/185] C#: Rename `Layout.Condition` to `FilePattern` and
move to separate file
---
.../Semmle.Extraction/FilePattern.cs | 34 +++++++++++++++
csharp/extractor/Semmle.Extraction/Layout.cs | 42 ++++---------------
2 files changed, 42 insertions(+), 34 deletions(-)
create mode 100644 csharp/extractor/Semmle.Extraction/FilePattern.cs
diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs
new file mode 100644
index 00000000000..b3aab69b181
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs
@@ -0,0 +1,34 @@
+using System.IO;
+
+namespace Semmle.Extraction
+{
+ ///
+ /// An file pattern, as used in either an extractor layout file or
+ /// a path transformer file.
+ ///
+ class FilePattern
+ {
+ private readonly bool include;
+ private readonly string prefix;
+
+ public bool Include => include;
+
+ public string Prefix => prefix;
+
+ public FilePattern(string line)
+ {
+ include = false;
+ if (line.StartsWith("-"))
+ line = line.Substring(1);
+ else
+ include = true;
+ prefix = Normalize(line.Trim());
+ }
+
+ static public string Normalize(string path)
+ {
+ path = Path.GetFullPath(path);
+ return path.Replace('\\', '/');
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs
index 9ab7ed5738a..1f772635df5 100644
--- a/csharp/extractor/Semmle.Extraction/Layout.cs
+++ b/csharp/extractor/Semmle.Extraction/Layout.cs
@@ -61,7 +61,7 @@ namespace Semmle.Extraction
///
/// The source file.
/// A newly created TrapWriter.
- public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) =>
+ public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) =>
new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression);
}
@@ -167,33 +167,7 @@ namespace Semmle.Extraction
sealed class LayoutBlock
{
- struct Condition
- {
- private readonly bool include;
- private readonly string prefix;
-
- public bool Include => include;
-
- public string Prefix => prefix;
-
- public Condition(string line)
- {
- include = false;
- if (line.StartsWith("-"))
- line = line.Substring(1);
- else
- include = true;
- prefix = Normalise(line.Trim());
- }
-
- static public string Normalise(string path)
- {
- path = Path.GetFullPath(path);
- return path.Replace('\\', '/');
- }
- }
-
- private readonly List conditions = new List();
+ private readonly List filePatterns = new List();
public readonly Layout.SubProject Directories;
@@ -219,20 +193,20 @@ namespace Semmle.Extraction
ReadVariable("ODASA_BUILD_ERROR_DIR", lines[i++]);
while (i < lines.Length && !lines[i].StartsWith("#"))
{
- conditions.Add(new Condition(lines[i++]));
+ filePatterns.Add(new FilePattern(lines[i++]));
}
}
public bool Matches(string path)
{
bool matches = false;
- path = Condition.Normalise(path);
- foreach (Condition condition in conditions)
+ path = FilePattern.Normalize(path);
+ foreach (var filePattern in filePatterns)
{
- if (condition.Include)
- matches |= path.StartsWith(condition.Prefix);
+ if (filePattern.Include)
+ matches |= path.StartsWith(filePattern.Prefix);
else
- matches &= !path.StartsWith(condition.Prefix);
+ matches &= !path.StartsWith(filePattern.Prefix);
}
return matches;
}
From 14567f531408c47f9e7b58ef9b0a34bb2c5e4c65 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Mon, 7 Sep 2020 08:57:12 +0200
Subject: [PATCH 003/185] C#: Support wild-cards in file patterns
Implements the specification at https://wiki.semmle.com/display/SDmaster/project-layout+format
by compiling file path specifications to regular expressions.
---
.../Semmle.Extraction.Tests/FilePattern.cs | 48 +++++++
.../Semmle.Extraction/FilePattern.cs | 125 +++++++++++++++---
csharp/extractor/Semmle.Extraction/Layout.cs | 14 +-
3 files changed, 157 insertions(+), 30 deletions(-)
create mode 100644 csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs
diff --git a/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs
new file mode 100644
index 00000000000..dfff75ea18b
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.Tests/FilePattern.cs
@@ -0,0 +1,48 @@
+using Xunit;
+
+namespace Semmle.Extraction.Tests
+{
+ public class FilePatternTests
+ {
+ [Fact]
+ public void TestRegexCompilation()
+ {
+ var fp = new FilePattern("/hadoop*");
+ Assert.Equal("^hadoop[^/]*.*", fp.RegexPattern);
+ fp = new FilePattern("**/org/apache/hadoop");
+ Assert.Equal("^.*/org/apache/hadoop.*", fp.RegexPattern);
+ fp = new FilePattern("hadoop-common/**/test// ");
+ Assert.Equal("^hadoop-common/.*/test(?/).*", fp.RegexPattern);
+ fp = new FilePattern(@"-C:\agent\root\asdf//");
+ Assert.Equal("^C:/agent/root/asdf(?/).*", fp.RegexPattern);
+ fp = new FilePattern(@"-C:\agent+\[root]\asdf//");
+ Assert.Equal(@"^C:/agent\+/\[root]/asdf(?/).*", fp.RegexPattern);
+ }
+
+ [Fact]
+ public void TestMatching()
+ {
+ var fp1 = new FilePattern(@"C:\agent\root\abc//");
+ var fp2 = new FilePattern(@"C:\agent\root\def//ghi");
+ var patterns = new[] { fp1, fp2 };
+
+ var success = FilePattern.Matches(patterns, @"C:\agent\root\abc\file.cs", out var s);
+ Assert.True(success);
+ Assert.Equal("/file.cs", s);
+
+ success = FilePattern.Matches(patterns, @"C:\agent\root\def\ghi\file.cs", out s);
+ Assert.True(success);
+ Assert.Equal("/ghi/file.cs", s);
+
+ success = FilePattern.Matches(patterns, @"C:\agent\root\def\file.cs", out s);
+ Assert.False(success);
+ }
+
+ [Fact]
+ public void TestInvalidPatterns()
+ {
+ Assert.Throws(() => new FilePattern("/abc//def//ghi"));
+ Assert.Throws(() => new FilePattern("/abc**def"));
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs
index b3aab69b181..c648faadbe1 100644
--- a/csharp/extractor/Semmle.Extraction/FilePattern.cs
+++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs
@@ -1,34 +1,125 @@
-using System.IO;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Diagnostics.CodeAnalysis;
+using Semmle.Util;
namespace Semmle.Extraction
{
+ public sealed class InvalidFilePatternException : Exception
+ {
+ public InvalidFilePatternException(string pattern, string message) :
+ base($"Invalid file pattern '{pattern}': {message}")
+ { }
+ }
+
///
/// An file pattern, as used in either an extractor layout file or
/// a path transformer file.
///
- class FilePattern
+ public sealed class FilePattern
{
- private readonly bool include;
- private readonly string prefix;
+ ///
+ /// Whether this is an inclusion pattern.
+ ///
+ public bool Include { get; }
- public bool Include => include;
-
- public string Prefix => prefix;
-
- public FilePattern(string line)
+ public FilePattern(string pattern)
{
- include = false;
- if (line.StartsWith("-"))
- line = line.Substring(1);
+ Include = false;
+ if (pattern.StartsWith("-"))
+ pattern = pattern.Substring(1);
else
- include = true;
- prefix = Normalize(line.Trim());
+ Include = true;
+ pattern = FileUtils.ConvertToUnix(pattern.Trim()).TrimStart('/');
+ RegexPattern = BuildRegex(pattern).ToString();
}
- static public string Normalize(string path)
+ ///
+ /// Constructs a regex string from a file pattern. Throws
+ /// `InvalidFilePatternException` for invalid patterns.
+ ///
+ static StringBuilder BuildRegex(string pattern)
{
- path = Path.GetFullPath(path);
- return path.Replace('\\', '/');
+ bool HasCharAt(int i, Predicate p) =>
+ i >= 0 && i < pattern.Length && p(pattern[i]);
+ var sb = new StringBuilder();
+ var i = 0;
+ var seenDoubleSlash = false;
+ sb.Append('^');
+ while (i < pattern.Length)
+ {
+ if (pattern[i] == '/')
+ {
+ if (HasCharAt(i + 1, c => c == '/'))
+ {
+ if (seenDoubleSlash)
+ throw new InvalidFilePatternException(pattern, "'//' is allowed at most once.");
+ sb.Append("(?/)");
+ i += 2;
+ seenDoubleSlash = true;
+ }
+ else
+ {
+ sb.Append('/');
+ i++;
+ }
+ }
+ else if (pattern[i] == '*')
+ {
+ if (HasCharAt(i + 1, c => c == '*'))
+ {
+ if (HasCharAt(i - 1, c => c != '/'))
+ throw new InvalidFilePatternException(pattern, "'**' preceeded by non-`/` character.");
+ if (HasCharAt(i + 2, c => c != '/'))
+ throw new InvalidFilePatternException(pattern, "'**' succeeded by non-`/` character");
+ sb.Append(".*");
+ i += 2;
+ }
+ else
+ {
+ sb.Append("[^/]*");
+ i++;
+ }
+ }
+ else
+ sb.Append(Regex.Escape(pattern[i++].ToString()));
+ }
+ return sb.Append(".*");
+ }
+
+
+ ///
+ /// The regex pattern compiled from this file pattern.
+ ///
+ public string RegexPattern { get; }
+
+ ///
+ /// Returns `true` if the set of file patterns `patterns` match the path `path`.
+ /// If so, `transformerSuffix` will contain the part of `path` that needs to be
+ /// suffixed when using path transformers.
+ ///
+ public static bool Matches(IEnumerable patterns, string path, [NotNullWhen(true)] out string? transformerSuffix)
+ {
+ path = FileUtils.ConvertToUnix(path).TrimStart('/');
+ Match? lastMatch = null;
+ foreach (var pattern in patterns)
+ {
+ var m = new Regex(pattern.RegexPattern).Match(path);
+ if (m.Success)
+ lastMatch = pattern.Include ? m : null;
+ }
+ if (lastMatch is Match)
+ {
+ transformerSuffix = lastMatch.Groups.TryGetValue("doubleslash", out var group)
+ ? path.Substring(group.Index)
+ : path;
+ return true;
+ }
+
+ transformerSuffix = null;
+ return false;
}
}
}
\ No newline at end of file
diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs
index 1f772635df5..1e44c3142e4 100644
--- a/csharp/extractor/Semmle.Extraction/Layout.cs
+++ b/csharp/extractor/Semmle.Extraction/Layout.cs
@@ -197,18 +197,6 @@ namespace Semmle.Extraction
}
}
- public bool Matches(string path)
- {
- bool matches = false;
- path = FilePattern.Normalize(path);
- foreach (var filePattern in filePatterns)
- {
- if (filePattern.Include)
- matches |= path.StartsWith(filePattern.Prefix);
- else
- matches &= !path.StartsWith(filePattern.Prefix);
- }
- return matches;
- }
+ public bool Matches(string path) => FilePattern.Matches(filePatterns, path, out var _);
}
}
From 37f1ce312256e36553c3951fba3c5c0c8f40ad9c Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Mon, 7 Sep 2020 08:57:49 +0200
Subject: [PATCH 004/185] C#: Implement support for path transformers
---
.../Semmle.Extraction.CIL/Context.cs | 2 +-
.../Entities/Assembly.cs | 10 +-
.../Semmle.Extraction.CIL/Entities/File.cs | 27 +--
.../Semmle.Extraction.CIL/Entities/Folder.cs | 20 +-
.../Semmle.Extraction.CIL/Factories.cs | 4 +-
.../Semmle.Extraction.CSharp/Analyser.cs | 26 ++-
.../Entities/Compilation.cs | 13 +-
.../Semmle.Extraction.CSharp/Extractor.cs | 13 +-
.../Semmle.Extraction.Tests/Layout.cs | 84 +++++----
.../PathTransformer.cs | 45 +++++
.../Semmle.Extraction.Tests/TrapWriter.cs | 27 +--
.../Semmle.Extraction/Entities/File.cs | 89 +++------
.../Semmle.Extraction/Entities/Folder.cs | 43 ++---
.../extractor/Semmle.Extraction/Extractor.cs | 12 +-
csharp/extractor/Semmle.Extraction/Layout.cs | 13 +-
.../Semmle.Extraction/PathTransformer.cs | 178 ++++++++++++++++++
.../extractor/Semmle.Extraction/TrapWriter.cs | 44 ++---
.../Semmle.Util/CanonicalPathCache.cs | 23 +++
18 files changed, 442 insertions(+), 231 deletions(-)
create mode 100644 csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
create mode 100644 csharp/extractor/Semmle.Extraction/PathTransformer.cs
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.cs
index 79fb8b7a8d4..6b8b01b109a 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Context.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Context.cs
@@ -37,7 +37,7 @@ namespace Semmle.Extraction.CIL
namespaceFactory = new CachedFunction(n => CreateNamespace(mdReader.GetString(n)));
namespaceDefinitionFactory = new CachedFunction(CreateNamespace);
sourceFiles = new CachedFunction(path => new Entities.PdbSourceFile(this, path));
- folders = new CachedFunction(path => new Entities.Folder(this, path));
+ folders = new CachedFunction(path => new Entities.Folder(this, path));
sourceLocations = new CachedFunction(location => new Entities.PdbSourceLocation(this, location));
defaultGenericContext = new EmptyContext(this);
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
index 255d37699f2..2504d7e7954 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs
@@ -5,6 +5,7 @@ using Semmle.Util.Logging;
using System;
using Semmle.Extraction.Entities;
using System.IO;
+using Semmle.Util;
namespace Semmle.Extraction.CIL.Entities
{
@@ -134,9 +135,12 @@ namespace Semmle.Extraction.CIL.Entities
extracted = false;
try
{
- var extractor = new Extractor(false, assemblyPath, logger);
- var project = layout.LookupProjectOrDefault(assemblyPath);
- using (var trapWriter = project.CreateTrapWriter(logger, assemblyPath + ".cil", true, trapCompression))
+ var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
+ var pathTransformer = new PathTransformer(canonicalPathCache);
+ var extractor = new Extractor(false, assemblyPath, logger, pathTransformer);
+ var transformedAssemblyPath = pathTransformer.Transform(assemblyPath);
+ var project = layout.LookupProjectOrDefault(transformedAssemblyPath);
+ using (var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), true, trapCompression))
{
trapFile = trapWriter.TrapFile;
if (nocache || !System.IO.File.Exists(trapFile))
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
index bc8c4c8c76d..b164fad1acd 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
@@ -13,33 +13,38 @@ namespace Semmle.Extraction.CIL.Entities
public class File : LabelledEntity, IFile
{
- protected readonly string path;
+ protected readonly string OriginalPath;
+ protected readonly PathTransformer.ITransformedPath TransformedPath;
public File(Context cx, string path) : base(cx)
{
- this.path = Semmle.Extraction.Entities.File.PathAsDatabaseString(path);
+ this.OriginalPath = path;
+ TransformedPath = cx.cx.Extractor.PathTransformer.Transform(OriginalPath);
}
public override void WriteId(TextWriter trapFile)
{
- trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
+ trapFile.Write(TransformedPath.DatabaseId);
}
public override bool Equals(object obj)
{
- return GetType() == obj.GetType() && path == ((File)obj).path;
+ return GetType() == obj.GetType() && OriginalPath == ((File)obj).OriginalPath;
}
- public override int GetHashCode() => 11 * path.GetHashCode();
+ public override int GetHashCode() => 11 * OriginalPath.GetHashCode();
public override IEnumerable Contents
{
get
{
- var parent = cx.CreateFolder(System.IO.Path.GetDirectoryName(path));
- yield return parent;
- yield return Tuples.containerparent(parent, this);
- yield return Tuples.files(this, path, System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetExtension(path).Substring(1));
+ if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir)
+ {
+ var parent = cx.CreateFolder(dir);
+ yield return parent;
+ yield return Tuples.containerparent(parent, this);
+ }
+ yield return Tuples.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension);
}
}
@@ -65,9 +70,9 @@ namespace Semmle.Extraction.CIL.Entities
var text = file.Contents;
if (text == null)
- cx.cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", path));
+ cx.cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", OriginalPath));
else
- cx.cx.TrapWriter.Archive(path, text);
+ cx.cx.TrapWriter.Archive(TransformedPath, text);
yield return Tuples.file_extraction_mode(this, 2);
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
index 48ebe6a19d1..f95492bb0e4 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
@@ -9,16 +9,16 @@ namespace Semmle.Extraction.CIL.Entities
public sealed class Folder : LabelledEntity, IFolder
{
- readonly string path;
+ readonly PathTransformer.ITransformedPath TransformedPath;
- public Folder(Context cx, string path) : base(cx)
+ public Folder(Context cx, PathTransformer.ITransformedPath path) : base(cx)
{
- this.path = path;
+ this.TransformedPath = path;
}
public override void WriteId(TextWriter trapFile)
{
- trapFile.Write(Semmle.Extraction.Entities.File.PathAsDatabaseId(path));
+ trapFile.Write(TransformedPath.DatabaseId);
}
public override string IdSuffix => ";folder";
@@ -27,25 +27,21 @@ namespace Semmle.Extraction.CIL.Entities
{
get
{
- // On Posix, we could get a Windows directory of the form "C:"
- bool windowsDriveLetter = path.Length == 2 && char.IsLetter(path[0]) && path[1] == ':';
-
- var parent = Path.GetDirectoryName(path);
- if (parent != null && !windowsDriveLetter)
+ if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath parent)
{
var parentFolder = cx.CreateFolder(parent);
yield return parentFolder;
yield return Tuples.containerparent(parentFolder, this);
}
- yield return Tuples.folders(this, Semmle.Extraction.Entities.File.PathAsDatabaseString(path), Path.GetFileName(path));
+ yield return Tuples.folders(this, TransformedPath.Value, TransformedPath.NameWithoutExtension);
}
}
public override bool Equals(object obj)
{
- return obj is Folder folder && path == folder.path;
+ return obj is Folder folder && TransformedPath == folder.TransformedPath;
}
- public override int GetHashCode() => path.GetHashCode();
+ public override int GetHashCode() => TransformedPath.GetHashCode();
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
index f522521a845..5c37228a41e 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Factories.cs
@@ -201,7 +201,7 @@ namespace Semmle.Extraction.CIL
#region Locations
readonly CachedFunction sourceFiles;
- readonly CachedFunction folders;
+ readonly CachedFunction folders;
readonly CachedFunction sourceLocations;
///
@@ -216,7 +216,7 @@ namespace Semmle.Extraction.CIL
///
/// The path of the folder.
/// A folder entity.
- public Folder CreateFolder(string path) => folders[path];
+ public Folder CreateFolder(PathTransformer.ITransformedPath path) => folders[path];
///
/// Creates a source location.
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs
index 544a1819117..a2db3e4a2db 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs
@@ -25,12 +25,15 @@ namespace Semmle.Extraction.CSharp
public readonly ILogger Logger;
- public Analyser(IProgressMonitor pm, ILogger logger)
+ public readonly PathTransformer PathTransformer;
+
+ public Analyser(IProgressMonitor pm, ILogger logger, PathTransformer pathTransformer)
{
Logger = logger;
Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now);
stopWatch.Start();
progressMonitor = pm;
+ PathTransformer = pathTransformer;
}
CSharpCompilation compilation;
@@ -64,7 +67,7 @@ namespace Semmle.Extraction.CSharp
layout = new Layout();
this.options = options;
this.compilation = compilation;
- extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger);
+ extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger, PathTransformer);
LogDiagnostics();
SetReferencePaths();
@@ -114,7 +117,7 @@ namespace Semmle.Extraction.CSharp
{
compilation = compilationIn;
layout = new Layout();
- extractor = new Extraction.Extractor(true, null, Logger);
+ extractor = new Extraction.Extractor(true, null, Logger, PathTransformer);
this.options = options;
LogExtractorInfo(Extraction.Extractor.Version);
SetReferencePaths();
@@ -227,9 +230,10 @@ namespace Semmle.Extraction.CSharp
try
{
var assemblyPath = extractor.OutputPath;
+ var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
var assembly = compilation.Assembly;
- var projectLayout = layout.LookupProjectOrDefault(assemblyPath);
- var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression);
+ var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
+ var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression);
compilationTrapFile = trapWriter; // Dispose later
var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true));
@@ -257,8 +261,9 @@ namespace Semmle.Extraction.CSharp
stopwatch.Start();
var assemblyPath = r.FilePath;
- var projectLayout = layout.LookupProjectOrDefault(assemblyPath);
- using (var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression))
+ var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
+ var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
+ using (var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, true, options.TrapCompression))
{
var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile);
@@ -357,16 +362,17 @@ namespace Semmle.Extraction.CSharp
var stopwatch = new Stopwatch();
stopwatch.Start();
var sourcePath = tree.FilePath;
+ var transformedSourcePath = PathTransformer.Transform(sourcePath);
- var projectLayout = layout.LookupProjectOrNull(sourcePath);
+ var projectLayout = layout.LookupProjectOrNull(transformedSourcePath);
bool excluded = projectLayout == null;
- string trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, sourcePath, options.TrapCompression);
+ string trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression);
bool upToDate = false;
if (!excluded)
{
// compilation.Clone() is used to allow symbols to be garbage collected.
- using (var trapWriter = projectLayout.CreateTrapWriter(Logger, sourcePath, false, options.TrapCompression))
+ using (var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, false, options.TrapCompression))
{
upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs
index b6ff91f5988..9f96b03f9f3 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilation.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using Semmle.Util;
namespace Semmle.Extraction.CSharp.Entities
{
@@ -22,32 +23,32 @@ namespace Semmle.Extraction.CSharp.Entities
{
Extraction.Entities.Assembly.CreateOutputAssembly(cx);
- trapFile.compilations(this, Extraction.Entities.File.PathAsDatabaseString(cwd));
+ trapFile.compilations(this, FileUtils.ConvertToUnix(cwd));
// Arguments
int index = 0;
- foreach(var arg in args)
+ foreach (var arg in args)
{
trapFile.compilation_args(this, index++, arg);
}
// Files
index = 0;
- foreach(var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath)))
+ foreach (var file in cx.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(cx, tree.FilePath)))
{
trapFile.compilation_compiling_files(this, index++, file);
}
// References
index = 0;
- foreach(var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath)))
+ foreach (var file in cx.Compilation.References.OfType().Select(r => Extraction.Entities.File.Create(cx, r.FilePath)))
{
trapFile.compilation_referencing_files(this, index++, file);
}
// Diagnostics
index = 0;
- foreach(var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d)))
+ foreach (var diag in cx.Compilation.GetDiagnostics().Select(d => new Diagnostic(cx, d)))
{
trapFile.diagnostic_for(diag, this, 0, index++);
}
@@ -57,7 +58,7 @@ namespace Semmle.Extraction.CSharp.Entities
{
var trapFile = cx.TrapWriter.Writer;
int index = 0;
- foreach(float metric in p.Metrics)
+ foreach (float metric in p.Metrics)
{
trapFile.compilation_time(this, -1, index++, metric);
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs
index 7ca12e00f26..e775e422e8c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs
@@ -76,16 +76,16 @@ namespace Semmle.Extraction.CSharp
return ExitCode.Ok;
}
- using (var analyser = new Analyser(new LogProgressMonitor(logger), logger))
+ var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
+ var pathTransformer = new PathTransformer(canonicalPathCache);
+
+ using (var analyser = new Analyser(new LogProgressMonitor(logger), logger, pathTransformer))
using (var references = new BlockingCollection())
{
try
{
var compilerVersion = new CompilerVersion(commandLineArguments);
- bool preserveSymlinks = Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true";
- var canonicalPathCache = CanonicalPathCache.Create(logger, 1000, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow);
-
if (compilerVersion.SkipExtraction)
{
logger.Log(Severity.Warning, " Unrecognized compiler '{0}' because {1}", compilerVersion.SpecifiedCompiler, compilerVersion.SkipReason);
@@ -317,7 +317,10 @@ namespace Semmle.Extraction.CSharp
ILogger logger,
CommonOptions options)
{
- using (var analyser = new Analyser(pm, logger))
+ var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
+ var pathTransformer = new PathTransformer(canonicalPathCache);
+
+ using (var analyser = new Analyser(pm, logger, pathTransformer))
using (var references = new BlockingCollection())
{
try
diff --git a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs
index 49da0fd45c5..301b2d8a881 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/Layout.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/Layout.cs
@@ -5,6 +5,26 @@ using System.Runtime.InteropServices;
namespace Semmle.Extraction.Tests
{
+ struct TransformedPathStub : PathTransformer.ITransformedPath
+ {
+ readonly string value;
+ public TransformedPathStub(string value) => this.value = value;
+ public string Value => value;
+
+ public string Extension => throw new System.NotImplementedException();
+
+ public string NameWithoutExtension => throw new System.NotImplementedException();
+
+ public PathTransformer.ITransformedPath ParentDirectory => throw new System.NotImplementedException();
+
+ public string DatabaseId => throw new System.NotImplementedException();
+
+ public PathTransformer.ITransformedPath WithSuffix(string suffix)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+
public class Layout
{
readonly ILogger Logger = new LoggerMock();
@@ -13,10 +33,10 @@ namespace Semmle.Extraction.Tests
public void TestDefaultLayout()
{
var layout = new Semmle.Extraction.Layout(null, null, null);
- var project = layout.LookupProjectOrNull("foo.cs");
+ var project = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs"));
// All files are mapped when there's no layout file.
- Assert.True(layout.FileInLayout("foo.cs"));
+ Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs")));
// Test trap filename
var tmpDir = Path.GetTempPath();
@@ -28,13 +48,13 @@ namespace Semmle.Extraction.Tests
Assert.NotEqual(Directory.GetCurrentDirectory(), tmpDir);
return;
}
- var f1 = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip);
- var g1 = TrapWriter.NestPaths(Logger, tmpDir, "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE);
+ var f1 = project.GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
+ var g1 = TrapWriter.NestPaths(Logger, tmpDir, "foo.cs.trap.gz");
Assert.Equal(f1, g1);
// Test trap file generation
- var trapwriterFilename = project.GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip);
- using (var trapwriter = project.CreateTrapWriter(Logger, "foo.cs", false, TrapWriter.CompressionMode.Gzip))
+ var trapwriterFilename = project.GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
+ using (var trapwriter = project.CreateTrapWriter(Logger, new TransformedPathStub("foo.cs"), false, TrapWriter.CompressionMode.Gzip))
{
trapwriter.Emit("1=*");
Assert.False(File.Exists(trapwriterFilename));
@@ -63,23 +83,23 @@ namespace Semmle.Extraction.Tests
var layout = new Semmle.Extraction.Layout(null, null, "layout.txt");
// Test general pattern matching
- Assert.True(layout.FileInLayout("bar.cs"));
- Assert.False(layout.FileInLayout("foo.cs"));
- Assert.False(layout.FileInLayout("goo.cs"));
- Assert.False(layout.FileInLayout("excluded/bar.cs"));
- Assert.True(layout.FileInLayout("excluded/foo.cs"));
- Assert.True(layout.FileInLayout("included/foo.cs"));
+ Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
+ Assert.False(layout.FileInLayout(new TransformedPathStub("foo.cs")));
+ Assert.False(layout.FileInLayout(new TransformedPathStub("goo.cs")));
+ Assert.False(layout.FileInLayout(new TransformedPathStub("excluded/bar.cs")));
+ Assert.True(layout.FileInLayout(new TransformedPathStub("excluded/foo.cs")));
+ Assert.True(layout.FileInLayout(new TransformedPathStub("included/foo.cs")));
// Test the trap file
- var project = layout.LookupProjectOrNull("bar.cs");
- var trapwriterFilename = project.GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip);
- Assert.Equal(TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE),
+ var project = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs"));
+ var trapwriterFilename = project.GetTrapPath(Logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip);
+ Assert.Equal(TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz"),
trapwriterFilename);
// Test the source archive
- var trapWriter = project.CreateTrapWriter(Logger, "bar.cs", false, TrapWriter.CompressionMode.Gzip);
- trapWriter.Archive("layout.txt", System.Text.Encoding.ASCII);
- var writtenFile = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\archive"), "layout.txt", TrapWriter.InnerPathComputation.ABSOLUTE);
+ var trapWriter = project.CreateTrapWriter(Logger, new TransformedPathStub("bar.cs"), false, TrapWriter.CompressionMode.Gzip);
+ trapWriter.Archive("layout.txt", new TransformedPathStub("layout.txt"), System.Text.Encoding.ASCII);
+ var writtenFile = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\archive"), "layout.txt");
Assert.True(File.Exists(writtenFile));
File.Delete("layout.txt");
}
@@ -89,9 +109,9 @@ namespace Semmle.Extraction.Tests
{
// When you specify both a trap file and a layout, use the trap file.
var layout = new Semmle.Extraction.Layout(Path.GetFullPath("snapshot\\trap"), null, "something.txt");
- Assert.True(layout.FileInLayout("bar.cs"));
- var f1 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip);
- var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE);
+ Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
+ var f1 = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")).GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
+ var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz");
Assert.Equal(f1, g1);
}
@@ -117,26 +137,26 @@ namespace Semmle.Extraction.Tests
var layout = new Semmle.Extraction.Layout(null, null, "layout.txt");
// Use Section 2
- Assert.True(layout.FileInLayout("bar.cs"));
- var f1 = layout.LookupProjectOrNull("bar.cs").GetTrapPath(Logger, "bar.cs", TrapWriter.CompressionMode.Gzip);
- var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE);
+ Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
+ var f1 = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs")).GetTrapPath(Logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip);
+ var g1 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz");
Assert.Equal(f1, g1);
// Use Section 1
- Assert.True(layout.FileInLayout("foo.cs"));
- var f2 = layout.LookupProjectOrNull("foo.cs").GetTrapPath(Logger, "foo.cs", TrapWriter.CompressionMode.Gzip);
- var g2 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE);
+ Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs")));
+ var f2 = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs")).GetTrapPath(Logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
+ var g2 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz");
Assert.Equal(f2, g2);
// boo.dll is not in the layout, so use layout from first section.
- Assert.False(layout.FileInLayout("boo.dll"));
- var f3 = layout.LookupProjectOrDefault("boo.dll").GetTrapPath(Logger, "boo.dll", TrapWriter.CompressionMode.Gzip);
- var g3 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz", TrapWriter.InnerPathComputation.ABSOLUTE);
+ Assert.False(layout.FileInLayout(new TransformedPathStub("boo.dll")));
+ var f3 = layout.LookupProjectOrDefault(new TransformedPathStub("boo.dll")).GetTrapPath(Logger, new TransformedPathStub("boo.dll"), TrapWriter.CompressionMode.Gzip);
+ var g3 = TrapWriter.NestPaths(Logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz");
Assert.Equal(f3, g3);
// boo.cs is not in the layout, so return null
- Assert.False(layout.FileInLayout("boo.cs"));
- Assert.Null(layout.LookupProjectOrNull("boo.cs"));
+ Assert.False(layout.FileInLayout(new TransformedPathStub("boo.cs")));
+ Assert.Null(layout.LookupProjectOrNull(new TransformedPathStub("boo.cs")));
}
[Fact]
diff --git a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
new file mode 100644
index 00000000000..04865ceb8c1
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
@@ -0,0 +1,45 @@
+using Semmle.Util;
+using Xunit;
+
+namespace Semmle.Extraction.Tests
+{
+ class PathCacheStub : IPathCache
+ {
+ public string GetCanonicalPath(string path) => path;
+ }
+
+ public class PathTransformerTests
+ {
+ [Fact]
+ public void TestTransformerFile()
+ {
+ var spec = new string[]
+ {
+ @"#D:\src",
+ @"C:\agent*\src//",
+ @"-C:\agent*\src\external",
+ @"",
+ @"#empty",
+ @"",
+ @"#src2",
+ @"/agent*//src",
+ @"",
+ @"#optsrc",
+ @"opt/src//"
+ };
+
+ var pathTransformer = new PathTransformer(new PathCacheStub(), spec);
+
+ // Windows-style matching
+ Assert.Equal(@"C:\bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value);
+ Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value);
+ Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value);
+ Assert.Equal(@"C:\agent43\src\external\file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value);
+
+ // Linux-style matching
+ Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value);
+ Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent42/src/file.cs").Value);
+ Assert.Equal(@"optsrc/file.cs", pathTransformer.Transform(@"/opt/src/file.cs").Value);
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs
index fd7f77f427b..54da865689b 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/TrapWriter.cs
@@ -14,7 +14,7 @@ namespace Semmle.Extraction.Tests
string tempDir = System.IO.Path.GetTempPath();
string root1, root2, root3;
- if(Win32.IsWindows())
+ if (Win32.IsWindows())
{
root1 = "E:";
root2 = "e:";
@@ -27,32 +27,21 @@ namespace Semmle.Extraction.Tests
root3 = "/";
}
- string formattedTempDir = tempDir.Replace('/', '\\').Replace(':', '_').Trim('\\');
-
var logger = new LoggerMock();
- System.IO.Directory.SetCurrentDirectory(tempDir);
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- // `Directory.SetCurrentDirectory()` doesn't seem to work on macOS,
- // so disable this test on macOS, for now
- Assert.NotEqual(Directory.GetCurrentDirectory(), tempDir);
- return;
- }
+ Assert.Equal($@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\'));
- Assert.Equal($@"C:\Temp\source_archive\{formattedTempDir}\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/','\\'));
+ Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs").Replace('/', '\\'));
- Assert.Equal(@"C:\Temp\source_archive\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", "def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\'));
+ Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs").Replace('/', '\\'));
- Assert.Equal(@"C:\Temp\source_archive\E_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root1}\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\'));
+ Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs").Replace('/', '\\'));
- Assert.Equal(@"C:\Temp\source_archive\e_\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root2}\source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\'));
+ Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\'));
- Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\'));
+ Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs").Replace('/', '\\'));
- Assert.Equal(@"C:\Temp\source_archive\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}source\def.cs", TrapWriter.InnerPathComputation.RELATIVE).Replace('/', '\\'));
-
- Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs", TrapWriter.InnerPathComputation.ABSOLUTE).Replace('/', '\\'));
+ Assert.Equal(@"C:\Temp\source_archive\diskstation\share\source\def.cs", TrapWriter.NestPaths(logger, @"C:\Temp\source_archive", $@"{root3}{root3}diskstation\share\source\def.cs").Replace('/', '\\'));
}
class LoggerMock : ILogger
diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs
index cbdf1535fbb..a0e9ea6d725 100644
--- a/csharp/extractor/Semmle.Extraction/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs
@@ -10,93 +10,54 @@ namespace Semmle.Extraction.Entities
File(Context cx, string path)
: base(cx, path)
{
- Path = path;
+ OriginalPath = path;
+ TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath);
}
- public string Path
- {
- get;
- private set;
- }
+ readonly string OriginalPath;
+ readonly PathTransformer.ITransformedPath TransformedPath;
- public string DatabasePath => PathAsDatabaseId(Path);
-
- public override bool NeedsPopulation => Context.DefinesFile(Path) || Path == Context.Extractor.OutputPath;
+ public override bool NeedsPopulation => Context.DefinesFile(OriginalPath) || OriginalPath == Context.Extractor.OutputPath;
public override void Populate(TextWriter trapFile)
{
- if (Path == null)
+ trapFile.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension);
+
+ if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir)
+ trapFile.containerparent(Folder.Create(Context, dir), this);
+
+ var fromSource = TransformedPath.Extension.ToLowerInvariant().Equals("cs");
+ if (fromSource)
{
- trapFile.files(this, "", "", "");
- }
- else
- {
- var fi = new FileInfo(Path);
-
- string extension = fi.Extension ?? "";
- string name = fi.Name;
- name = name.Substring(0, name.Length - extension.Length);
- int fromSource = extension.ToLowerInvariant().Equals(".cs") ? 1 : 2;
-
- // remove the dot from the extension
- if (extension.Length > 0)
- extension = extension.Substring(1);
- trapFile.files(this, PathAsDatabaseString(Path), name, extension);
-
- trapFile.containerparent(Folder.Create(Context, fi.Directory), this);
- if (fromSource == 1)
+ foreach (var text in Context.Compilation.SyntaxTrees.
+ Where(t => t.FilePath == OriginalPath).
+ Select(tree => tree.GetText()))
{
- foreach (var text in Context.Compilation.SyntaxTrees.
- Where(t => t.FilePath == Path).
- Select(tree => tree.GetText()))
- {
- var rawText = text.ToString() ?? "";
- var lineCounts = LineCounter.ComputeLineCounts(rawText);
- if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++;
+ var rawText = text.ToString() ?? "";
+ var lineCounts = LineCounter.ComputeLineCounts(rawText);
+ if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') lineCounts.Total++;
- trapFile.numlines(this, lineCounts);
- Context.TrapWriter.Archive(fi.FullName, text.Encoding ?? System.Text.Encoding.Default);
- }
+ trapFile.numlines(this, lineCounts);
+ Context.TrapWriter.Archive(OriginalPath, TransformedPath, text.Encoding ?? System.Text.Encoding.Default);
}
-
- trapFile.file_extraction_mode(this, Context.Extractor.Standalone ? 1 : 0);
}
+
+ trapFile.file_extraction_mode(this, Context.Extractor.Standalone ? 1 : 0);
}
public override void WriteId(System.IO.TextWriter trapFile)
{
- if (Path is null)
- trapFile.Write("GENERATED;sourcefile");
- else
- {
- trapFile.Write(DatabasePath);
- trapFile.Write(";sourcefile");
- }
+ trapFile.Write(TransformedPath.DatabaseId);
+ trapFile.Write(";sourcefile");
}
- ///
- /// Converts a path string into a string to use as an ID
- /// in the QL database.
- ///
- /// An absolute path.
- /// The database ID.
- public static string PathAsDatabaseId(string path)
- {
- if (path.Length >= 2 && path[1] == ':' && Char.IsLower(path[0]))
- path = Char.ToUpper(path[0]) + "_" + path.Substring(2);
- return path.Replace('\\', '/').Replace(":", "_");
- }
-
- public static string PathAsDatabaseString(string path) => path.Replace('\\', '/');
-
public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, path);
public static File CreateGenerated(Context cx) => GeneratedFile.Create(cx);
class GeneratedFile : File
{
- GeneratedFile(Context cx)
- : base(cx, "") { }
+ GeneratedFile(Context cx) : base(cx, "") { }
public override bool NeedsPopulation => true;
diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
index 3c29ee38bd9..0e275e37d8d 100644
--- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
+++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
@@ -2,65 +2,44 @@ using System.IO;
namespace Semmle.Extraction.Entities
{
- sealed class Folder : CachedEntity
+ sealed class Folder : CachedEntity
{
- Folder(Context cx, DirectoryInfo init)
- : base(cx, init)
- {
- Path = init.FullName;
- }
-
- public string Path
- {
- get;
- private set;
- }
-
- public string DatabasePath => File.PathAsDatabaseId(Path);
+ Folder(Context cx, PathTransformer.ITransformedPath init) : base(cx, init) { }
public override void Populate(TextWriter trapFile)
{
- // Ensure that the name of the root directory is consistent
- // with the XmlTrapWriter.
- // Linux/Windows: java.io.File.getName() returns ""
- // On Linux: System.IO.DirectoryInfo.Name returns "/"
- // On Windows: System.IO.DirectoryInfo.Name returns "L:\"
- string shortName = symbol.Parent == null ? "" : symbol.Name;
-
- trapFile.folders(this, File.PathAsDatabaseString(Path), shortName);
- if (symbol.Parent != null)
- {
- trapFile.containerparent(Create(Context, symbol.Parent), this);
- }
+ trapFile.folders(this, symbol.Value, symbol.NameWithoutExtension);
+ if (symbol.ParentDirectory is PathTransformer.ITransformedPath parent)
+ trapFile.containerparent(Create(Context, parent), this);
}
public override bool NeedsPopulation => true;
public override void WriteId(System.IO.TextWriter trapFile)
{
- trapFile.Write(DatabasePath);
+ trapFile.Write(symbol.DatabaseId);
trapFile.Write(";folder");
}
- public static Folder Create(Context cx, DirectoryInfo folder) =>
+ public static Folder Create(Context cx, PathTransformer.ITransformedPath folder) =>
FolderFactory.Instance.CreateEntity2(cx, folder);
public override Microsoft.CodeAnalysis.Location? ReportingLocation => null;
- class FolderFactory : ICachedEntityFactory
+ class FolderFactory : ICachedEntityFactory
{
public static readonly FolderFactory Instance = new FolderFactory();
- public Folder Create(Context cx, DirectoryInfo init) => new Folder(cx, init);
+ public Folder Create(Context cx, PathTransformer.ITransformedPath init) => new Folder(cx, init);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
- public override int GetHashCode() => Path.GetHashCode();
+ public override int GetHashCode() => symbol.GetHashCode();
public override bool Equals(object? obj)
{
- return obj is Folder folder && folder.Path == Path;
+ return obj is Folder folder && Equals(folder.symbol, symbol);
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction/Extractor.cs b/csharp/extractor/Semmle.Extraction/Extractor.cs
index 13750c1aa5c..7b176f20c9f 100644
--- a/csharp/extractor/Semmle.Extraction/Extractor.cs
+++ b/csharp/extractor/Semmle.Extraction/Extractor.cs
@@ -81,6 +81,11 @@ namespace Semmle.Extraction
///
ILogger Logger { get; }
+ ///
+ /// The path transformer to apply.
+ ///
+ PathTransformer PathTransformer { get; }
+
///
/// Creates a new context.
///
@@ -111,11 +116,14 @@ namespace Semmle.Extraction
///
/// If the extraction is standalone.
/// The name of the output DLL/EXE, or null if not specified (standalone extraction).
- public Extractor(bool standalone, string outputPath, ILogger logger)
+ /// The object used for logging.
+ /// The object used for path transformations.
+ public Extractor(bool standalone, string outputPath, ILogger logger, PathTransformer pathTransformer)
{
Standalone = standalone;
OutputPath = outputPath;
Logger = logger;
+ PathTransformer = pathTransformer;
}
// Limit the number of error messages in the log file
@@ -205,5 +213,7 @@ namespace Semmle.Extraction
public ILogger Logger { get; private set; }
public static string Version => $"{ThisAssembly.Git.BaseTag} ({ThisAssembly.Git.Sha})";
+
+ public PathTransformer PathTransformer { get; }
}
}
diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs
index 1e44c3142e4..d740d2c05b9 100644
--- a/csharp/extractor/Semmle.Extraction/Layout.cs
+++ b/csharp/extractor/Semmle.Extraction/Layout.cs
@@ -54,14 +54,15 @@ namespace Semmle.Extraction
///
/// The source file.
/// The full filepath of the trap file.
- public string GetTrapPath(ILogger logger, string srcFile, TrapWriter.CompressionMode trapCompression) => TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression);
+ public string GetTrapPath(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression) =>
+ TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression);
///
/// Creates a trap writer for a given source/assembly file.
///
/// The source file.
/// A newly created TrapWriter.
- public TrapWriter CreateTrapWriter(ILogger logger, string srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) =>
+ public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, bool discardDuplicates, TrapWriter.CompressionMode trapCompression) =>
new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, discardDuplicates, trapCompression);
}
@@ -73,7 +74,7 @@ namespace Semmle.Extraction
///
/// The file to look up.
/// The relevant subproject, or null if not found.
- public SubProject? LookupProjectOrNull(string sourceFile)
+ public SubProject? LookupProjectOrNull(PathTransformer.ITransformedPath sourceFile)
{
if (!useLayoutFile) return DefaultProject;
@@ -89,7 +90,7 @@ namespace Semmle.Extraction
///
/// The file to look up.
/// The relevant subproject, or DefaultProject if not found.
- public SubProject LookupProjectOrDefault(string sourceFile)
+ public SubProject LookupProjectOrDefault(PathTransformer.ITransformedPath sourceFile)
{
return LookupProjectOrNull(sourceFile) ?? DefaultProject;
}
@@ -134,7 +135,7 @@ namespace Semmle.Extraction
///
/// The absolute path of the file to query.
/// True iff there is no layout file or the layout file specifies the file.
- public bool FileInLayout(string path) => LookupProjectOrNull(path) != null;
+ public bool FileInLayout(PathTransformer.ITransformedPath path) => LookupProjectOrNull(path) != null;
void ReadLayoutFile(string layout)
{
@@ -197,6 +198,6 @@ namespace Semmle.Extraction
}
}
- public bool Matches(string path) => FilePattern.Matches(filePatterns, path, out var _);
+ public bool Matches(PathTransformer.ITransformedPath path) => FilePattern.Matches(filePatterns, path.Value, out var _);
}
}
diff --git a/csharp/extractor/Semmle.Extraction/PathTransformer.cs b/csharp/extractor/Semmle.Extraction/PathTransformer.cs
new file mode 100644
index 00000000000..3c972f48114
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction/PathTransformer.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Diagnostics.CodeAnalysis;
+using Semmle.Util;
+
+namespace Semmle.Extraction
+{
+ ///
+ /// A class for interpreting path transformers specified using the environment
+ /// variable `CODEQL_PATH_TRANSFORMER`.
+ ///
+ public sealed class PathTransformer
+ {
+ public class InvalidPathTransformerException : Exception
+ {
+ public InvalidPathTransformerException(string message) :
+ base($"Invalid path transformer specification: {message}")
+ { }
+ }
+
+ ///
+ /// A transformed path.
+ ///
+ public interface ITransformedPath
+ {
+ string Value { get; }
+
+ string Extension { get; }
+
+ string NameWithoutExtension { get; }
+
+ ITransformedPath? ParentDirectory { get; }
+
+ ITransformedPath WithSuffix(string suffix);
+
+ string DatabaseId { get; }
+ }
+
+ struct TransformedPath : ITransformedPath
+ {
+ public TransformedPath(string value) { this.value = value; }
+ readonly string value;
+
+ public string Value => value;
+
+ public string Extension => Path.GetExtension(value)?.Substring(1) ?? "";
+
+ public string NameWithoutExtension => Path.GetFileNameWithoutExtension(value);
+
+ public ITransformedPath? ParentDirectory
+ {
+ get
+ {
+ var dir = Path.GetDirectoryName(value);
+ if (dir is null)
+ return null;
+ var isWindowsDriveLetter = dir.Length == 2 && char.IsLetter(dir[0]) && dir[1] == ':';
+ if (isWindowsDriveLetter)
+ return null;
+ return new TransformedPath(FileUtils.ConvertToUnix(dir));
+ }
+ }
+
+ public ITransformedPath WithSuffix(string suffix) => new TransformedPath(value + suffix);
+
+ public string DatabaseId
+ {
+ get
+ {
+ var ret = value;
+ if (ret.Length >= 2 && ret[1] == ':' && Char.IsLower(ret[0]))
+ ret = Char.ToUpper(ret[0]) + "_" + ret.Substring(2);
+ return ret.Replace('\\', '/').Replace(":", "_");
+ }
+ }
+
+ public override int GetHashCode() => 11 * value.GetHashCode();
+
+ public override bool Equals(object? obj) => obj is TransformedPath tp && tp.value == value;
+
+ public override string ToString() => value;
+ }
+
+ readonly Func transform;
+
+ ///
+ /// Returns the path obtained by transforming `path`.
+ ///
+ public ITransformedPath Transform(string path) => new TransformedPath(transform(path));
+
+ ///
+ /// Default constructor reads parameters from the environment.
+ ///
+ public PathTransformer(IPathCache pathCache) :
+ this(pathCache, Environment.GetEnvironmentVariable("CODEQL_PATH_TRANSFORMER") is string file ? File.ReadAllLines(file) : null)
+ {
+ }
+
+ ///
+ /// Creates a path transformer based on the specification in `lines`.
+ /// Throws `InvalidPathTransformerException` for invalid specifications.
+ ///
+ public PathTransformer(IPathCache pathCache, string[]? lines)
+ {
+ if (lines is null)
+ {
+ transform = path => FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path));
+ return;
+ }
+
+ var sections = ParsePathTransformerSpec(lines);
+ transform = path =>
+ {
+ path = FileUtils.ConvertToUnix(pathCache.GetCanonicalPath(path));
+ foreach (var section in sections)
+ {
+ if (section.Matches(path, out var transformed))
+ return transformed;
+ }
+ return path;
+ };
+ }
+
+ static IEnumerable ParsePathTransformerSpec(string[] lines)
+ {
+ var sections = new List();
+ try
+ {
+ int i = 0;
+ while (i < lines.Length && !lines[i].StartsWith("#"))
+ i++;
+ while (i < lines.Length)
+ {
+ var section = new TransformerSection(lines, ref i);
+ sections.Add(section);
+ }
+
+ if (sections.Count == 0)
+ throw new InvalidPathTransformerException("contains no sections.");
+ }
+ catch (InvalidFilePatternException ex)
+ {
+ throw new InvalidPathTransformerException(ex.Message);
+ }
+ return sections;
+ }
+ }
+
+ sealed class TransformerSection
+ {
+ readonly string name;
+ readonly List filePatterns = new List();
+
+ public TransformerSection(string[] lines, ref int i)
+ {
+ name = lines[i++].Substring(1);
+ while (i < lines.Length && !lines[i].StartsWith("#"))
+ {
+ if (string.IsNullOrEmpty(lines[i]))
+ i++;
+ else
+ filePatterns.Add(new FilePattern(lines[i++]));
+ }
+ }
+
+ public bool Matches(string path, [NotNullWhen(true)] out string? transformed)
+ {
+ if (FilePattern.Matches(filePatterns, path, out var suffix))
+ {
+ transformed = FileUtils.ConvertToUnix(name) + suffix;
+ return true;
+ }
+ transformed = null;
+ return false;
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction/TrapWriter.cs b/csharp/extractor/Semmle.Extraction/TrapWriter.cs
index 7ea08eafc1c..8082567c825 100644
--- a/csharp/extractor/Semmle.Extraction/TrapWriter.cs
+++ b/csharp/extractor/Semmle.Extraction/TrapWriter.cs
@@ -14,12 +14,6 @@ namespace Semmle.Extraction
public sealed class TrapWriter : IDisposable
{
- public enum InnerPathComputation
- {
- ABSOLUTE,
- RELATIVE
- }
-
public enum CompressionMode
{
None,
@@ -45,7 +39,7 @@ namespace Semmle.Extraction
readonly CompressionMode TrapCompression;
- public TrapWriter(ILogger logger, string outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression)
+ public TrapWriter(ILogger logger, PathTransformer.ITransformedPath outputfile, string? trap, string? archive, bool discardDuplicates, CompressionMode trapCompression)
{
Logger = logger;
TrapCompression = trapCompression;
@@ -107,16 +101,17 @@ namespace Semmle.Extraction
/// Adds the specified input file to the source archive. It may end up in either the normal or long path area
/// of the source archive, depending on the length of its full path.
///
- /// The path to the input file.
+ /// The path to the input file.
+ /// The transformed path to the input file.
/// The encoding used by the input file.
- public void Archive(string inputPath, Encoding inputEncoding)
+ public void Archive(string originalPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding)
{
if (string.IsNullOrEmpty(archive)) return;
// Calling GetFullPath makes this use the canonical capitalisation, if the file exists.
- string fullInputPath = Path.GetFullPath(inputPath);
+ string fullInputPath = Path.GetFullPath(originalPath);
- ArchivePath(fullInputPath, inputEncoding);
+ ArchivePath(fullInputPath, transformedPath, inputEncoding);
}
///
@@ -124,14 +119,11 @@ namespace Semmle.Extraction
///
/// The path of the file.
/// The contents of the file.
- public void Archive(string inputPath, string contents)
+ public void Archive(PathTransformer.ITransformedPath inputPath, string contents)
{
if (string.IsNullOrEmpty(archive)) return;
- // Calling GetFullPath makes this use the canonical capitalisation, if the file exists.
- string fullInputPath = Path.GetFullPath(inputPath);
-
- ArchiveContents(fullInputPath, contents);
+ ArchiveContents(inputPath, contents);
}
///
@@ -210,18 +202,19 @@ namespace Semmle.Extraction
/// source archive less than the system path limit of 260 characters.
///
/// The full path to the input file.
+ /// The transformed path to the input file.
/// The encoding used by the input file.
/// If the output path in the source archive would
/// exceed the system path limit of 260 characters.
- private void ArchivePath(string fullInputPath, Encoding inputEncoding)
+ private void ArchivePath(string fullInputPath, PathTransformer.ITransformedPath transformedPath, Encoding inputEncoding)
{
string contents = File.ReadAllText(fullInputPath, inputEncoding);
- ArchiveContents(fullInputPath, contents);
+ ArchiveContents(transformedPath, contents);
}
- private void ArchiveContents(string fullInputPath, string contents)
+ private void ArchiveContents(PathTransformer.ITransformedPath transformedPath, string contents)
{
- string dest = NestPaths(Logger, archive, fullInputPath, InnerPathComputation.ABSOLUTE);
+ string dest = NestPaths(Logger, archive, transformedPath.Value);
string tmpSrcFile = Path.GetTempFileName();
File.WriteAllText(tmpSrcFile, contents, UTF8);
try
@@ -236,14 +229,11 @@ namespace Semmle.Extraction
}
}
- public static string NestPaths(ILogger logger, string? outerpath, string innerpath, InnerPathComputation innerPathComputation)
+ public static string NestPaths(ILogger logger, string? outerpath, string innerpath)
{
string nested = innerpath;
if (!string.IsNullOrEmpty(outerpath))
{
- if (!Path.IsPathRooted(innerpath) && innerPathComputation == InnerPathComputation.ABSOLUTE)
- innerpath = Path.GetFullPath(innerpath);
-
// Remove all leading path separators / or \
// For example, UNC paths have two leading \\
innerpath = innerpath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
@@ -276,13 +266,13 @@ namespace Semmle.Extraction
}
}
- public static string TrapPath(ILogger logger, string? folder, string filename, TrapWriter.CompressionMode trapCompression)
+ public static string TrapPath(ILogger logger, string? folder, PathTransformer.ITransformedPath path, TrapWriter.CompressionMode trapCompression)
{
- filename = $"{Path.GetFullPath(filename)}.trap{TrapExtension(trapCompression)}";
+ var filename = $"{path.Value}.trap{TrapExtension(trapCompression)}";
if (string.IsNullOrEmpty(folder))
folder = Directory.GetCurrentDirectory();
- return NestPaths(logger, folder, filename, InnerPathComputation.ABSOLUTE); ;
+ return NestPaths(logger, folder, filename);
}
}
}
diff --git a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
index bbc8ab995b4..339641ecb35 100644
--- a/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
+++ b/csharp/extractor/Semmle.Util/CanonicalPathCache.cs
@@ -222,6 +222,29 @@ namespace Semmle.Util
this.pathStrategy = pathStrategy;
}
+
+ ///
+ /// Create a CanonicalPathCache.
+ ///
+ ///
+ ///
+ /// Creates the appropriate PathStrategy object which encapsulates
+ /// the correct algorithm. Falls back to different implementations
+ /// depending on platform.
+ ///
+ ///
+ /// Size of the cache.
+ /// Policy for following symlinks.
+ /// A new CanonicalPathCache.
+ public static CanonicalPathCache Create(ILogger logger, int maxCapacity)
+ {
+ var preserveSymlinks =
+ Environment.GetEnvironmentVariable("CODEQL_PRESERVE_SYMLINKS") == "true" ||
+ Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true";
+ return Create(logger, maxCapacity, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow);
+
+ }
+
///
/// Create a CanonicalPathCache.
///
From 4d0a1ee8578a061f3286b6e4b7a626e4d9211921 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 8 Sep 2020 09:29:35 +0200
Subject: [PATCH 005/185] Address review comments
---
.../PathTransformer.cs | 4 +--
.../Semmle.Extraction/FilePattern.cs | 34 +++++++++++--------
.../Semmle.Extraction/PathTransformer.cs | 11 +++---
3 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
index 04865ceb8c1..b0f0ba8c51f 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/PathTransformer.cs
@@ -31,10 +31,10 @@ namespace Semmle.Extraction.Tests
var pathTransformer = new PathTransformer(new PathCacheStub(), spec);
// Windows-style matching
- Assert.Equal(@"C:\bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value);
+ Assert.Equal(@"C:/bar.cs", pathTransformer.Transform(@"C:\bar.cs").Value);
Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent42\src\file.cs").Value);
Assert.Equal("D:/src/file.cs", pathTransformer.Transform(@"C:\agent43\src\file.cs").Value);
- Assert.Equal(@"C:\agent43\src\external\file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value);
+ Assert.Equal(@"C:/agent43/src/external/file.cs", pathTransformer.Transform(@"C:\agent43\src\external\file.cs").Value);
// Linux-style matching
Assert.Equal(@"src2/src/file.cs", pathTransformer.Transform(@"/agent/src/file.cs").Value);
diff --git a/csharp/extractor/Semmle.Extraction/FilePattern.cs b/csharp/extractor/Semmle.Extraction/FilePattern.cs
index c648faadbe1..2d61badd2f2 100644
--- a/csharp/extractor/Semmle.Extraction/FilePattern.cs
+++ b/csharp/extractor/Semmle.Extraction/FilePattern.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics.CodeAnalysis;
@@ -15,7 +16,7 @@ namespace Semmle.Extraction
}
///
- /// An file pattern, as used in either an extractor layout file or
+ /// A file pattern, as used in either an extractor layout file or
/// a path transformer file.
///
public sealed class FilePattern
@@ -27,11 +28,12 @@ namespace Semmle.Extraction
public FilePattern(string pattern)
{
- Include = false;
+ Include = true;
if (pattern.StartsWith("-"))
+ {
pattern = pattern.Substring(1);
- else
- Include = true;
+ Include = false;
+ }
pattern = FileUtils.ConvertToUnix(pattern.Trim()).TrimStart('/');
RegexPattern = BuildRegex(pattern).ToString();
}
@@ -103,19 +105,23 @@ namespace Semmle.Extraction
public static bool Matches(IEnumerable patterns, string path, [NotNullWhen(true)] out string? transformerSuffix)
{
path = FileUtils.ConvertToUnix(path).TrimStart('/');
- Match? lastMatch = null;
- foreach (var pattern in patterns)
+
+ foreach (var pattern in patterns.Reverse())
{
var m = new Regex(pattern.RegexPattern).Match(path);
if (m.Success)
- lastMatch = pattern.Include ? m : null;
- }
- if (lastMatch is Match)
- {
- transformerSuffix = lastMatch.Groups.TryGetValue("doubleslash", out var group)
- ? path.Substring(group.Index)
- : path;
- return true;
+ {
+ if (pattern.Include)
+ {
+ transformerSuffix = m.Groups.TryGetValue("doubleslash", out var group)
+ ? path.Substring(group.Index)
+ : path;
+ return true;
+ }
+
+ transformerSuffix = null;
+ return false;
+ }
}
transformerSuffix = null;
diff --git a/csharp/extractor/Semmle.Extraction/PathTransformer.cs b/csharp/extractor/Semmle.Extraction/PathTransformer.cs
index 3c972f48114..2c9770e790e 100644
--- a/csharp/extractor/Semmle.Extraction/PathTransformer.cs
+++ b/csharp/extractor/Semmle.Extraction/PathTransformer.cs
@@ -154,13 +154,12 @@ namespace Semmle.Extraction
public TransformerSection(string[] lines, ref int i)
{
- name = lines[i++].Substring(1);
- while (i < lines.Length && !lines[i].StartsWith("#"))
+ name = lines[i++].Substring(1); // skip the '#'
+ for (; i < lines.Length && !lines[i].StartsWith("#"); i++)
{
- if (string.IsNullOrEmpty(lines[i]))
- i++;
- else
- filePatterns.Add(new FilePattern(lines[i++]));
+ var line = lines[i];
+ if (!string.IsNullOrWhiteSpace(line))
+ filePatterns.Add(new FilePattern(line));
}
}
From b1e6e3a6f23c9f6c03c4f8b752aa5f6a38112da1 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 8 Sep 2020 14:18:20 +0200
Subject: [PATCH 006/185] Java: Add 1.25 change notes.
---
change-notes/1.25/analysis-java.md | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md
index 7cdd9e491a2..5adb02e4d47 100644
--- a/change-notes/1.25/analysis-java.md
+++ b/change-notes/1.25/analysis-java.md
@@ -4,6 +4,8 @@ The following changes in version 1.25 affect Java analysis in all applications.
## General improvements
+The Java autobuilder has been improved to detect more Gradle Java versions.
+
## New queries
| **Query** | **Tags** | **Purpose** |
@@ -14,10 +16,20 @@ The following changes in version 1.25 affect Java analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
-
+| Hard-coded credential in API call (`java/hardcoded-credential-api-call`) | More results | The query now recognizes the `BasicAWSCredentials` class of the Amazon client SDK library with hardcoded access key/secret key. |
+| Deserialization of user-controlled data (`java/unsafe-deserialization`) | Fewer false positive results | The query no longer reports results using `org.apache.commons.io.serialization.ValidatingObjectInputStream`. |
+| Use of a broken or risky cryptographic algorithm (`java/weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. |
+| Use of a potentially broken or risky cryptographic algorithm (`java/potentially-weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. |
+| Reading from a world writable file (`java/world-writable-file-read`) | More results | The query now recognizes more JDK file operations. |
## Changes to libraries
+* The data-flow library has been improved with more taint flow modeling for the
+ Collections framework and other classes of the JDK. This affects all security
+ queries using data flow and can yield additional results.
+* The data-flow library has been improved with more taint flow modeling for the
+ Spring framework. This affects all security queries using data flow and can
+ yield additional results on project that rely on the Spring framework.
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `sink()` via the method
@@ -39,3 +51,5 @@ The following changes in version 1.25 affect Java analysis in all applications.
}
}
```
+* The library has been extended with more support for Java 14 features
+ (`switch` expressions and pattern-matching for `instanceof`).
From 2979f9813ee0ba08779623a72106f00ab35e30d8 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 8 Sep 2020 14:27:12 +0200
Subject: [PATCH 007/185] Python: Add missing change notes
I looked through PRs between rc/1.24 and rc/1.25 and added missing change notes for:
- https://github.com/github/codeql/pull/3314
- https://github.com/github/codeql/pull/3302
- https://github.com/github/codeql/pull/3212
- https://github.com/github/codeql/pull/3453
- https://github.com/github/codeql/pull/3407
- https://github.com/github/codeql/pull/3563
```
git log --grep="Merge pull request" --format=oneline rc/1.24..rc/1.25 -- python/
```
---
change-notes/1.25/analysis-python.md | 25 ++++++-------------------
1 file changed, 6 insertions(+), 19 deletions(-)
diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md
index 5d0fc69ec80..345cce5a227 100644
--- a/change-notes/1.25/analysis-python.md
+++ b/change-notes/1.25/analysis-python.md
@@ -1,22 +1,9 @@
# Improvements to Python analysis
-The following changes in version 1.25 affect Python analysis in all applications.
-
-## General improvements
-
-
-## New queries
-
-| **Query** | **Tags** | **Purpose** |
-|-----------------------------|-----------|--------------------------------------------------------------------|
-
-
-## Changes to existing queries
-
-| **Query** | **Expected impact** | **Change** |
-|----------------------------|------------------------|------------------------------------------------------------------|
-
-
-## Changes to libraries
-
* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
+* Added model of taint sources for HTTP servers using `http.server`.
+* Added taint modeling of routed parameters in flask.
+* Improved modeling of builtin methods on strings for taint tracking.
+* Improved classification of test files.
+* New class `BoundMethodValue` exposing information about a bound method.
+* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries.
From 02da80aa25fc7690bef9a4c77c057571b2c27406 Mon Sep 17 00:00:00 2001
From: Anders Schack-Mulligen
Date: Tue, 8 Sep 2020 14:40:33 +0200
Subject: [PATCH 008/185] Java: Remove "New Queries" section.
---
change-notes/1.25/analysis-java.md | 6 ------
1 file changed, 6 deletions(-)
diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md
index 5adb02e4d47..ab11e5aaaf1 100644
--- a/change-notes/1.25/analysis-java.md
+++ b/change-notes/1.25/analysis-java.md
@@ -6,12 +6,6 @@ The following changes in version 1.25 affect Java analysis in all applications.
The Java autobuilder has been improved to detect more Gradle Java versions.
-## New queries
-
-| **Query** | **Tags** | **Purpose** |
-|-----------------------------|-----------|--------------------------------------------------------------------|
-
-
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
From 1f4028f4a08a7faa99b614a6af6ecc36ef2eeb66 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Tue, 8 Sep 2020 16:24:57 +0200
Subject: [PATCH 009/185] Java: Add new SQL sinks for Hibernate versions 4 and
6
---
.../semmle/code/java/frameworks/Hibernate.qll | 30 ++++++++++++++-----
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
index 1366b3cac45..5ad0448249f 100644
--- a/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Hibernate.qll
@@ -4,20 +4,36 @@
import java
+/** The interface `org.hibernate.query.QueryProducer`. */
+class HibernateQueryProducer extends RefType {
+ HibernateQueryProducer() { this.hasQualifiedName("org.hibernate.query", "QueryProducer") }
+}
+
+/** The interface `org.hibernate.SharedSessionContract`. */
+class HibernateSharedSessionContract extends RefType {
+ HibernateSharedSessionContract() {
+ this.hasQualifiedName("org.hibernate", "SharedSessionContract")
+ }
+}
+
/** The interface `org.hibernate.Session`. */
class HibernateSession extends RefType {
HibernateSession() { this.hasQualifiedName("org.hibernate", "Session") }
}
/**
- * Holds if `m` is a method on `HibernateSession`, or a subclass, taking an SQL
- * string as its first argument.
+ * Holds if `m` is a method on `HibernateQueryProducer`, or `HibernateSharedSessionContract`
+ * or `HibernateSession`, or a subclass, taking an SQL string as its first argument.
*/
predicate hibernateSqlMethod(Method m) {
- m.getDeclaringType().getASourceSupertype*() instanceof HibernateSession and
+ exists(RefType t |
+ t = m.getDeclaringType().getASourceSupertype*() and
+ (
+ t instanceof HibernateQueryProducer or
+ t instanceof HibernateSharedSessionContract or
+ t instanceof HibernateSession
+ )
+ ) and
m.getParameterType(0) instanceof TypeString and
- (
- m.hasName("createQuery") or
- m.hasName("createSQLQuery")
- )
+ m.hasName(["createQuery", "createNativeQuery", "createSQLQuery"])
}
From 983f54f11a74736eed61075c24229032131d09a2 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 25 Aug 2020 15:21:16 -0700
Subject: [PATCH 010/185] C++: simple tests for vector output iterators
---
.../dataflow/taint-tests/localTaint.expected | 82 +++++++++++++++++++
.../dataflow/taint-tests/vector.cpp | 27 ++++++
2 files changed, 109 insertions(+)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 9a9fca0cd59..bca6b6299ad 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3173,3 +3173,85 @@
| vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
| vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | |
| vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | |
+| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | |
+| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | |
+| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT |
+| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | |
+| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | |
+| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | |
+| vector.cpp:334:22:334:24 | call to vector | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | |
+| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | |
+| vector.cpp:334:38:334:40 | call to vector | vector.cpp:354:1:354:1 | v3 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | |
+| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT |
+| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | |
+| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | |
+| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT |
+| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | |
+| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
+| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
+| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT |
+| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | |
+| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | |
+| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | |
+| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
+| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
+| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT |
+| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT |
+| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | |
+| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | |
+| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | |
+| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | |
+| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | |
+| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT |
+| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT |
+| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | |
+| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to begin | TAINT |
+| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to end | TAINT |
+| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | call to end | vector.cpp:345:15:345:15 | (__end) | |
+| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
+| vector.cpp:345:15:345:15 | ref arg (__range) | vector.cpp:345:15:345:15 | (__range) | |
+| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
+| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
+| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT |
+| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | |
+| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:354:1:354:1 | v3 | |
+| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT |
+| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | |
+| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | |
+| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | |
+| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT |
+| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT |
+| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | |
+| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
+| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | |
+| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | |
+| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | |
+| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index e2d12bed6c1..6360438663a 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -325,3 +325,30 @@ void test_constructors_more() {
sink(v3);
sink(v4); // tainted
}
+
+void taint_vector_output_iterator(std::vector::iterator iter) {
+ *iter = source();
+}
+
+void test_vector_output_iterator() {
+ std::vector v1(10), v2(10), v3(10), v4(10);
+
+ std::vector::iterator i1 = v1.begin();
+ *i1 = source();
+ sink(v1); // tainted [NOT DETECTED]
+
+ for(std::vector::iterator it = v2.begin(); it != v2.end(); ++it) {
+ *it = source(); // tainted [NOT DETECTED]
+ }
+ sink(v2);
+
+ for(int& x : v3) {
+ x = source();
+ }
+ sink(v3); // tainted [NOT DETECTED]
+
+ for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) {
+ *it = source();
+ }
+ sink(v4); // tainted [NOT DETECTED]
+}
From 703db0b9a6ffdb53af4cef150cf1ae6f10ca4517 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 25 Aug 2020 15:26:18 -0700
Subject: [PATCH 011/185] C++: noisy output iterators in AST taint tracking
---
.../dataflow/internal/TaintTrackingUtil.qll | 31 +++++++++++++++++++
.../cpp/models/implementations/Iterator.qll | 12 +++++++
.../dataflow/taint-tests/localTaint.expected | 11 +++++++
.../dataflow/taint-tests/taint.expected | 3 ++
.../dataflow/taint-tests/test_diff.expected | 3 ++
.../dataflow/taint-tests/vector.cpp | 10 +++---
6 files changed, 65 insertions(+), 5 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index a3f0c435e16..c15f39c0c15 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -10,6 +10,7 @@
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.Taint
+private import semmle.code.cpp.models.implementations.Iterator
private module DataFlow {
import semmle.code.cpp.dataflow.internal.DataFlowUtil
@@ -186,6 +187,12 @@ private predicate exprToExprStep(Expr exprIn, Expr exprOut) {
exprIn = call.getQualifier()
)
)
+ or
+ exists(Variable iterator, Variable collection |
+ assignmentViaIterator(iterator, exprIn) and
+ isIteratorForCollection(iterator, collection) and
+ collection.getAnAccess() = exprOut
+ )
}
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
@@ -249,3 +256,27 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) {
)
)
}
+
+private predicate isIteratorForCollection(Variable iterator, Variable collection) {
+ exists(Call beginOrEnd |
+ beginOrEnd.getTarget() instanceof BeginOrEndFunction and
+ beginOrEnd.getQualifier() = collection.getAnAccess() and
+ iterator.getAnAssignedValue() = beginOrEnd
+ )
+}
+
+private predicate assignmentViaIterator(Variable iterator, Expr rvalue) {
+ exists(Assignment a, Call c |
+ c.getTarget() instanceof IteratorArrayMemberOperator and
+ c.getQualifier() = iterator.getAnAccess()
+ or
+ c.getTarget() instanceof IteratorPointerDereferenceMemberOperator and
+ c.getQualifier() = iterator.getAnAccess()
+ or
+ c.getTarget() instanceof IteratorPointerDereferenceOperator and
+ c.getArgument(0) = iterator.getAnAccess()
+ |
+ c = a.getLValue() and
+ rvalue = a.getRValue()
+ )
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
index 2ad464d1634..758d093bedc 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
@@ -271,3 +271,15 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction {
output.isReturnValue()
}
}
+
+class BeginOrEndFunction extends MemberFunction, TaintFunction {
+ BeginOrEndFunction() {
+ this.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) and
+ this.getType().getUnspecifiedType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index bca6b6299ad..0c0362206e1 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3197,7 +3197,10 @@
| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | |
| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | |
| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT |
+| vector.cpp:337:8:337:13 | call to source | vector.cpp:336:34:336:35 | v1 | TAINT |
| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | |
+| vector.cpp:337:8:337:13 | call to source | vector.cpp:338:7:338:8 | v1 | TAINT |
+| vector.cpp:337:8:337:13 | call to source | vector.cpp:354:1:354:1 | v1 | TAINT |
| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
@@ -3216,7 +3219,11 @@
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | |
| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | |
| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:38:340:39 | v2 | TAINT |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:56:340:57 | v2 | TAINT |
| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:343:7:343:8 | v2 | TAINT |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:354:1:354:1 | v2 | TAINT |
| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT |
@@ -3253,5 +3260,9 @@
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | |
| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | |
| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:38:350:39 | v4 | TAINT |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:56:350:57 | v4 | TAINT |
| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:353:7:353:8 | v4 | TAINT |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:354:1:354:1 | v4 | TAINT |
| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 15744c35716..6697e57e4a3 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -326,3 +326,6 @@
| vector.cpp:312:7:312:7 | d | vector.cpp:303:14:303:19 | call to source |
| vector.cpp:324:7:324:8 | v2 | vector.cpp:318:15:318:20 | call to source |
| vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source |
+| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source |
+| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source |
+| vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index d4383d4e900..7533d079093 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -275,3 +275,6 @@
| vector.cpp:312:7:312:7 | vector.cpp:303:14:303:19 | AST only |
| vector.cpp:324:7:324:8 | vector.cpp:318:15:318:20 | AST only |
| vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only |
+| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only |
+| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only |
+| vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 6360438663a..f1cf4e91457 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -21,7 +21,7 @@ void test_range_based_for_loop_vector(int source1) {
}
for(std::vector::iterator it = v.begin(); it != v.end(); ++it) {
- sink(*it); // tainted
+ sink(*it); // tainted [NOT DETECTED by IR]
}
for(int& x : v) {
@@ -335,12 +335,12 @@ void test_vector_output_iterator() {
std::vector::iterator i1 = v1.begin();
*i1 = source();
- sink(v1); // tainted [NOT DETECTED]
+ sink(v1); // tainted [NOT DETECTED by IR]
for(std::vector::iterator it = v2.begin(); it != v2.end(); ++it) {
- *it = source(); // tainted [NOT DETECTED]
+ *it = source();
}
- sink(v2);
+ sink(v2); // tainted [NOT DETECTED by IR]
for(int& x : v3) {
x = source();
@@ -350,5 +350,5 @@ void test_vector_output_iterator() {
for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) {
*it = source();
}
- sink(v4); // tainted [NOT DETECTED]
+ sink(v4); // tainted [NOT DETECTED by IR]
}
From c8cdf68bf9b8881faae02f89ca3cb5091ec1c926 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 25 Aug 2020 16:34:36 -0700
Subject: [PATCH 012/185] C++: Remove StdStringBeginEnd
---
.../cpp/models/implementations/Iterator.qll | 4 ++++
.../cpp/models/implementations/StdString.qll | 17 -----------------
2 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
index 758d093bedc..93a04d5ef90 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
@@ -272,6 +272,10 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction {
}
}
+/**
+ * A `begin` or `end` member function, or a related member function, that
+ * returns an iterator.
+ */
class BeginOrEndFunction extends MemberFunction, TaintFunction {
BeginOrEndFunction() {
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) and
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
index 8e7cb24be73..3a2c914eb4c 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
@@ -212,23 +212,6 @@ class StdStringAssign extends TaintFunction {
}
}
-/**
- * The standard functions `std::string.begin` and `std::string.end` and their
- * variants.
- */
-class StdStringBeginEnd extends TaintFunction {
- StdStringBeginEnd() {
- this
- .hasQualifiedName("std", "basic_string",
- ["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"])
- }
-
- override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isQualifierObject() and
- output.isReturnValue()
- }
-}
-
/**
* The standard function `std::string.copy`.
*/
From 13c45b6664a6b4fcad59a60a096236cb702d21b0 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 27 Aug 2020 15:09:01 -0700
Subject: [PATCH 013/185] C++: remove unnecessary parameter in FlowVar.qll
---
.../code/cpp/dataflow/internal/FlowVar.qll | 25 ++++++++-----------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 6dc5da2f20b..83941140fa6 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -240,7 +240,7 @@ module FlowVar_internal {
(
initializer(v, sbb.getANode())
or
- assignmentLikeOperation(sbb, v, _, _)
+ assignmentLikeOperation(sbb, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb))
or
@@ -359,7 +359,7 @@ module FlowVar_internal {
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
- assignmentLikeOperation(node, v, _, e) and
+ assignmentLikeOperation(node, v, e) and
node = sbb
or
// We pick the defining `ControlFlowNode` of an `Initializer` to be its
@@ -449,7 +449,7 @@ module FlowVar_internal {
pragma[noinline]
private Variable getAVariableAssignedInLoop() {
exists(BasicBlock bbAssign |
- assignmentLikeOperation(bbAssign.getANode(), result, _, _) and
+ assignmentLikeOperation(bbAssign.getANode(), result, _) and
this.bbInLoop(bbAssign)
)
}
@@ -487,7 +487,7 @@ module FlowVar_internal {
pragma[noinline]
private predicate assignsToVar(BasicBlock bb, Variable v) {
- assignmentLikeOperation(bb.getANode(), v, _, _) and
+ assignmentLikeOperation(bb.getANode(), v, _) and
exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable())
}
@@ -524,7 +524,7 @@ module FlowVar_internal {
result = mid.getASuccessor() and
variableLiveInSBB(result, v) and
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
- not assignmentLikeOperation(result, v, _, _)
+ not assignmentLikeOperation(result, v, _)
)
}
@@ -566,7 +566,7 @@ module FlowVar_internal {
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
*/
private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) {
- assignmentLikeOperation(sbb, v, _, _)
+ assignmentLikeOperation(sbb, v, _)
or
// Liveness of `v` is killed when going backwards from a block that declares it
exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds))
@@ -687,20 +687,18 @@ module FlowVar_internal {
* predicate.
*/
predicate assignmentLikeOperation(
- ControlFlowNode node, Variable v, VariableAccess va, Expr assignedExpr
+ ControlFlowNode node, Variable v, Expr assignedExpr
) {
// Together, the two following cases cover `Assignment`
node =
any(AssignExpr ae |
- va = ae.getLValue() and
- v = va.getTarget() and
+ v.getAnAccess() = ae.getLValue() and
assignedExpr = ae.getRValue()
)
or
node =
any(AssignOperation ao |
- va = ao.getLValue() and
- v = va.getTarget() and
+ v.getAnAccess() = ao.getLValue() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
@@ -712,8 +710,7 @@ module FlowVar_internal {
// `PrefixCrementOperation` is itself a source
node =
any(CrementOperation op |
- va = op.getOperand() and
- v = va.getTarget() and
+ v.getAnAccess() = op.getOperand() and
assignedExpr = op
)
}
@@ -749,7 +746,7 @@ module FlowVar_internal {
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
DataFlowSubBasicBlockCutNode() {
exists(Variable v | not fullySupportedSsaVariable(v) |
- assignmentLikeOperation(this, v, _, _)
+ assignmentLikeOperation(this, v, _)
or
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this))
// It is not necessary to cut the basic blocks at `Initializer` nodes
From eab1557e27cfefef7b826960e3840e5ba79279e1 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Tue, 8 Sep 2020 14:09:57 -0700
Subject: [PATCH 014/185] C++: output iterator flow via FlowVar
---
.../cpp/dataflow/internal/DataFlowUtil.qll | 20 ++-
.../code/cpp/dataflow/internal/FlowVar.qll | 98 +++++++++--
.../dataflow/internal/TaintTrackingUtil.qll | 34 +---
.../dataflow/taint-tests/localTaint.expected | 160 +++++++++++++++---
.../dataflow/taint-tests/taint.expected | 8 +
.../dataflow/taint-tests/test_diff.expected | 8 +
.../dataflow/taint-tests/vector.cpp | 32 +++-
7 files changed, 287 insertions(+), 73 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 6647dda4072..9705ca492ce 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -283,12 +283,12 @@ abstract class PostUpdateNode extends Node {
override Location getLocation() { result = getPreUpdateNode().getLocation() }
}
-private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
+private abstract class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
PartialDefinition pd;
- PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
-
- override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
+ PartialDefinitionNode() {
+ this = TPartialDefinitionNode(pd)
+ }
override Location getLocation() { result = pd.getActualLocation() }
@@ -297,6 +297,18 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
}
+private class VariablePartialDefinitionNode extends PartialDefinitionNode {
+ override VariablePartialDefinition pd;
+
+ override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
+}
+
+private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
+ override IteratorPartialDefinition pd;
+
+ override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
+}
+
/**
* A post-update node on the `e->f` in `f(&e->f)` (and other forms).
*/
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 83941140fa6..cb2cdd68f08 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -6,6 +6,7 @@ import cpp
private import semmle.code.cpp.controlflow.SSA
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
private import semmle.code.cpp.dataflow.internal.AddressFlow
+private import semmle.code.cpp.models.implementations.Iterator
/**
* A conceptual variable that is assigned only once, like an SSA variable. This
@@ -109,20 +110,16 @@ class FlowVar extends TFlowVar {
*/
private module PartialDefinitions {
class PartialDefinition extends Expr {
- Expr innerDefinedExpr;
ControlFlowNode node;
PartialDefinition() {
- exists(Expr convertedInner |
- valueToUpdate(convertedInner, this.getFullyConverted(), node) and
- innerDefinedExpr = convertedInner.getUnconverted() and
- not this instanceof Conversion
- )
+ valueToUpdate(_, this.getFullyConverted(), node) and
+ not this instanceof Conversion
}
- deprecated predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
+ deprecated predicate partiallyDefines(Variable v) { none() }
- deprecated predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
+ deprecated predicate partiallyDefinesThis(ThisExpr e) { none() }
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
@@ -133,10 +130,9 @@ private module PartialDefinitions {
* Holds if this `PartialDefinition` defines variable `v` at control-flow
* node `cfn`.
*/
- pragma[noinline]
+ pragma[noinline] // does this work with a dispred?
predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
- innerDefinedExpr = v.getAnAccess() and
- cfn = node
+ none()
}
/**
@@ -147,10 +143,7 @@ private module PartialDefinitions {
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
- predicate definesExpressions(Expr inner, Expr outer) {
- inner = innerDefinedExpr and
- outer = this
- }
+ predicate definesExpressions(Expr inner, Expr outer) { none() }
/**
* Gets the location of this element, adjusted to avoid unknown locations
@@ -166,6 +159,68 @@ private module PartialDefinitions {
}
}
+ class IteratorPartialDefinition extends PartialDefinition {
+ Variable collection;
+ Call innerDefinedExpr;
+
+ IteratorPartialDefinition() {
+ exists(Expr convertedInner |
+ valueToUpdate(convertedInner, this.getFullyConverted(), node) and
+ innerDefinedExpr = convertedInner.getUnconverted() and
+ innerDefinedExpr.getQualifier() = getAnIteratorAccess(collection) and
+ innerDefinedExpr.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ }
+
+ deprecated override predicate partiallyDefines(Variable v) { v = collection }
+
+ deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() }
+
+ override predicate definesExpressions(Expr inner, Expr outer) {
+ inner = innerDefinedExpr and
+ outer = this
+ }
+
+ override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
+ v = collection and
+ cfn = node
+ }
+ }
+
+ class VariablePartialDefinition extends PartialDefinition {
+ Expr innerDefinedExpr;
+
+ VariablePartialDefinition() {
+ exists(Expr convertedInner |
+ valueToUpdate(convertedInner, this.getFullyConverted(), node) and
+ innerDefinedExpr = convertedInner.getUnconverted() and
+ not this instanceof Conversion
+ )
+ }
+
+ deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
+
+ deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
+
+ /**
+ * Holds if this partial definition may modify `inner` (or what it points
+ * to) through `outer`. These expressions will never be `Conversion`s.
+ *
+ * For example, in `f(& (*a).x)`, there are two results:
+ * - `inner` = `... .x`, `outer` = `&...`
+ * - `inner` = `a`, `outer` = `*`
+ */
+ override predicate definesExpressions(Expr inner, Expr outer) {
+ inner = innerDefinedExpr and
+ outer = this
+ }
+
+ override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
+ innerDefinedExpr = v.getAnAccess() and
+ cfn = node
+ }
+ }
+
/**
* A partial definition that's a definition by reference.
*/
@@ -686,9 +741,7 @@ module FlowVar_internal {
* `node instanceof Initializer` is covered by `initializer` instead of this
* predicate.
*/
- predicate assignmentLikeOperation(
- ControlFlowNode node, Variable v, Expr assignedExpr
- ) {
+ predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) {
// Together, the two following cases cover `Assignment`
node =
any(AssignExpr ae |
@@ -715,6 +768,15 @@ module FlowVar_internal {
)
}
+ Expr getAnIteratorAccess(Variable collection) {
+ exists(Call c, SsaDefinition def, Variable iterator |
+ c.getQualifier() = collection.getAnAccess() and
+ c.getTarget() instanceof BeginOrEndFunction and
+ def.getAnUltimateDefiningValue(iterator) = c and
+ result = def.getAUse(iterator)
+ )
+ }
+
/**
* Holds if `v` is initialized to have value `assignedExpr`.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index c15f39c0c15..c8d963866a1 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -187,12 +187,6 @@ private predicate exprToExprStep(Expr exprIn, Expr exprOut) {
exprIn = call.getQualifier()
)
)
- or
- exists(Variable iterator, Variable collection |
- assignmentViaIterator(iterator, exprIn) and
- isIteratorForCollection(iterator, collection) and
- collection.getAnAccess() = exprOut
- )
}
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
@@ -255,28 +249,18 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) {
exprIn = call.getArgument(argInIndex)
)
)
-}
-
-private predicate isIteratorForCollection(Variable iterator, Variable collection) {
- exists(Call beginOrEnd |
- beginOrEnd.getTarget() instanceof BeginOrEndFunction and
- beginOrEnd.getQualifier() = collection.getAnAccess() and
- iterator.getAnAssignedValue() = beginOrEnd
+ or
+ exists(Assignment a |
+ iteratorDereference(exprOut) and
+ a.getLValue() = exprOut and
+ a.getRValue() = exprIn
)
}
-private predicate assignmentViaIterator(Variable iterator, Expr rvalue) {
- exists(Assignment a, Call c |
- c.getTarget() instanceof IteratorArrayMemberOperator and
- c.getQualifier() = iterator.getAnAccess()
+private predicate iteratorDereference(Call c) {
+ c.getTarget() instanceof IteratorArrayMemberOperator
or
- c.getTarget() instanceof IteratorPointerDereferenceMemberOperator and
- c.getQualifier() = iterator.getAnAccess()
+ c.getTarget() instanceof IteratorPointerDereferenceMemberOperator
or
- c.getTarget() instanceof IteratorPointerDereferenceOperator and
- c.getArgument(0) = iterator.getAnAccess()
- |
- c = a.getLValue() and
- rvalue = a.getRValue()
- )
+ c.getTarget() instanceof IteratorPointerDereferenceOperator
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 0c0362206e1..c0f34638554 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3176,55 +3176,75 @@
| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | |
| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | |
| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT |
+| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT |
| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | |
+| vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | |
| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | |
| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:334:22:334:24 | call to vector | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:334:22:334:24 | call to vector | vector.cpp:382:1:382:1 | v1 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:382:1:382:1 | v2 | |
| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | |
| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | |
-| vector.cpp:334:38:334:40 | call to vector | vector.cpp:354:1:354:1 | v3 | |
+| vector.cpp:334:38:334:40 | call to vector | vector.cpp:382:1:382:1 | v3 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | |
+| vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | |
+| vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | |
+| vector.cpp:334:54:334:56 | call to vector | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | |
+| vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | |
+| vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | |
+| vector.cpp:334:78:334:80 | call to vector | vector.cpp:382:1:382:1 | v8 | |
| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | |
| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT |
| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | |
+| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | |
+| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v1 | |
| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | |
| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT |
-| vector.cpp:337:8:337:13 | call to source | vector.cpp:336:34:336:35 | v1 | TAINT |
+| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT |
| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | |
-| vector.cpp:337:8:337:13 | call to source | vector.cpp:338:7:338:8 | v1 | TAINT |
-| vector.cpp:337:8:337:13 | call to source | vector.cpp:354:1:354:1 | v1 | TAINT |
-| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:354:1:354:1 | v1 | |
+| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | |
| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT |
| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT |
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | |
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | |
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | |
+| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | |
+| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | |
+| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v2 | |
| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | |
| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:38:340:39 | v2 | TAINT |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:340:56:340:57 | v2 | TAINT |
+| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT |
| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:343:7:343:8 | v2 | TAINT |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:354:1:354:1 | v2 | TAINT |
-| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:354:1:354:1 | v2 | |
+| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT |
| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | |
@@ -3242,27 +3262,119 @@
| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT |
| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | |
-| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:354:1:354:1 | v3 | |
+| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:382:1:382:1 | v3 | |
| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT |
| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | |
+| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | |
| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:38:350:39 | v4 | TAINT |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:350:56:350:57 | v4 | TAINT |
+| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:3 | call to operator* [post update] | TAINT |
| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:353:7:353:8 | v4 | TAINT |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:354:1:354:1 | v4 | TAINT |
-| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:354:1:354:1 | v4 | |
+| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | |
+| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
+| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT |
+| vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | |
+| vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | |
+| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | |
+| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
+| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | |
+| vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT |
+| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT |
+| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | |
+| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
+| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
+| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | |
+| vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT |
+| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT |
+| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | |
+| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | |
+| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
+| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
+| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT |
+| vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | |
+| vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT |
+| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT |
+| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | |
+| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
+| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
+| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
+| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT |
+| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT |
+| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | |
+| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | |
+| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT |
+| vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | |
+| vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | |
+| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | |
+| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | |
+| vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT |
+| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT |
+| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | |
+| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | |
+| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | |
+| vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT |
+| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT |
+| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | |
+| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
+| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | |
+| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
+| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT |
+| vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | |
+| vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | |
+| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | |
+| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
+| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | |
+| vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT |
+| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT |
+| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | |
+| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
+| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
+| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | |
+| vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT |
+| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT |
+| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | |
+| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 6697e57e4a3..c8ed520e639 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -329,3 +329,11 @@
| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source |
| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source |
| vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source |
+| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source |
+| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source |
+| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source |
+| vector.cpp:365:7:365:8 | v6 | vector.cpp:362:8:362:13 | call to source |
+| vector.cpp:370:8:370:9 | v7 | vector.cpp:369:9:369:14 | call to source |
+| vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source |
+| vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source |
+| vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 7533d079093..94f4cf9d2fe 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -278,3 +278,11 @@
| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only |
| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only |
| vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only |
+| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only |
+| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only |
+| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only |
+| vector.cpp:365:7:365:8 | vector.cpp:362:8:362:13 | AST only |
+| vector.cpp:370:8:370:9 | vector.cpp:369:9:369:14 | AST only |
+| vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only |
+| vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only |
+| vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index f1cf4e91457..35145c3d815 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -330,8 +330,8 @@ void taint_vector_output_iterator(std::vector::iterator iter) {
*iter = source();
}
-void test_vector_output_iterator() {
- std::vector v1(10), v2(10), v3(10), v4(10);
+void test_vector_output_iterator(int b) {
+ std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10);
std::vector::iterator i1 = v1.begin();
*i1 = source();
@@ -351,4 +351,32 @@ void test_vector_output_iterator() {
*it = source();
}
sink(v4); // tainted [NOT DETECTED by IR]
+
+ std::vector::iterator i5 = v5.begin();
+ *i5 = source();
+ sink(v5); // tainted [NOT DETECTED by IR]
+ *i5 = 1;
+ sink(v5); // tainted [NOT DETECTED by IR]
+
+ std::vector::iterator i6 = v6.begin();
+ *i6 = source();
+ sink(v6); // tainted [NOT DETECTED by IR]
+ v6 = std::vector(10);
+ sink(v6); // [FALSE POSITIVE in AST]
+
+ std::vector::iterator i7 = v7.begin();
+ if(b) {
+ *i7 = source();
+ sink(v7); // tainted [NOT DETECTED by IR]
+ } else {
+ *i7 = 1;
+ sink(v7);
+ }
+ sink(v7); // tainted [NOT DETECTED by IR]
+
+ std::vector::iterator i8 = v8.begin();
+ *i8 = source();
+ sink(v8); // tainted [NOT DETECTED by IR]
+ *i8 = 1;
+ sink(v8);
}
From 038688a55c5519a40c15950a323fdf0ea45d13ae Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 9 Sep 2020 10:34:40 +0200
Subject: [PATCH 015/185] Python: Minor updates to 1.25 change notes
backporting fixes from `@sj`
---
change-notes/1.25/analysis-python.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md
index 345cce5a227..12586e8aec0 100644
--- a/change-notes/1.25/analysis-python.md
+++ b/change-notes/1.25/analysis-python.md
@@ -2,8 +2,8 @@
* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
* Added model of taint sources for HTTP servers using `http.server`.
-* Added taint modeling of routed parameters in flask.
-* Improved modeling of builtin methods on strings for taint tracking.
+* Added taint modeling of routed parameters in Flask.
+* Improved modeling of built-in methods on strings for taint tracking.
* Improved classification of test files.
* New class `BoundMethodValue` exposing information about a bound method.
* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries.
From 2e187a51ae6db786322ed979e6fa449e494352a7 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 9 Sep 2020 12:45:06 -0700
Subject: [PATCH 016/185] C++: test for interprocedurl iterator flow
---
.../dataflow/taint-tests/localTaint.expected | 12 +++---------
.../dataflow/taint-tests/taint.expected | 1 -
.../dataflow/taint-tests/test_diff.expected | 1 -
.../library-tests/dataflow/taint-tests/vector.cpp | 2 +-
4 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index c0f34638554..13d38363b35 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3269,7 +3269,7 @@
| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | |
-| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:4:351:5 | it | |
+| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
@@ -3277,14 +3277,8 @@
| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
-| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:4:351:5 | it | |
-| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:351:3:351:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v4 | |
-| vector.cpp:351:3:351:16 | ... = ... | vector.cpp:351:3:351:3 | call to operator* [post update] | |
-| vector.cpp:351:4:351:5 | it | vector.cpp:351:3:351:3 | call to operator* | TAINT |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:3 | call to operator* [post update] | TAINT |
-| vector.cpp:351:9:351:14 | call to source | vector.cpp:351:3:351:16 | ... = ... | |
+| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | |
+| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | |
| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index c8ed520e639..6759dd90d60 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -328,7 +328,6 @@
| vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source |
| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source |
| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source |
-| vector.cpp:353:7:353:8 | v4 | vector.cpp:351:9:351:14 | call to source |
| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source |
| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source |
| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 94f4cf9d2fe..8fbeb0c246c 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -277,7 +277,6 @@
| vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only |
| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only |
| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only |
-| vector.cpp:353:7:353:8 | vector.cpp:351:9:351:14 | AST only |
| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only |
| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only |
| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 35145c3d815..153385a202d 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -348,7 +348,7 @@ void test_vector_output_iterator(int b) {
sink(v3); // tainted [NOT DETECTED]
for(std::vector::iterator it = v4.begin(); it != v4.end(); ++it) {
- *it = source();
+ taint_vector_output_iterator(it);
}
sink(v4); // tainted [NOT DETECTED by IR]
From 10633019a6081ee9b169d3a022d2132789e057af Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 9 Sep 2020 12:45:17 -0700
Subject: [PATCH 017/185] C++: autoformat
---
.../semmle/code/cpp/dataflow/internal/FlowVar.qll | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index cb2cdd68f08..c6929358543 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -130,10 +130,9 @@ private module PartialDefinitions {
* Holds if this `PartialDefinition` defines variable `v` at control-flow
* node `cfn`.
*/
- pragma[noinline] // does this work with a dispred?
- predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
- none()
- }
+ // does this work with a dispred?
+ pragma[noinline]
+ predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { none() }
/**
* Holds if this partial definition may modify `inner` (or what it points
@@ -180,7 +179,7 @@ private module PartialDefinitions {
inner = innerDefinedExpr and
outer = this
}
-
+
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
v = collection and
cfn = node
@@ -198,7 +197,9 @@ private module PartialDefinitions {
)
}
- deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
+ deprecated override predicate partiallyDefines(Variable v) {
+ innerDefinedExpr = v.getAnAccess()
+ }
deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
@@ -214,7 +215,7 @@ private module PartialDefinitions {
inner = innerDefinedExpr and
outer = this
}
-
+
override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
innerDefinedExpr = v.getAnAccess() and
cfn = node
From 3414063f2eedf3444c65cf3232193e4270476e2c Mon Sep 17 00:00:00 2001
From: Calum Grant <42069085+calumgrant@users.noreply.github.com>
Date: Fri, 11 Sep 2020 13:16:26 +0100
Subject: [PATCH 018/185] Update change-notes/1.25/analysis-python.md
Co-authored-by: Rasmus Wriedt Larsen
---
change-notes/1.25/analysis-python.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md
index 12586e8aec0..ed3496bc734 100644
--- a/change-notes/1.25/analysis-python.md
+++ b/change-notes/1.25/analysis-python.md
@@ -5,5 +5,5 @@
* Added taint modeling of routed parameters in Flask.
* Improved modeling of built-in methods on strings for taint tracking.
* Improved classification of test files.
-* New class `BoundMethodValue` exposing information about a bound method.
+* New class `BoundMethodValue` represents a bound method during runtime.
* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries.
From eb5782d90822bcf6f3551aa599c4e0a0dde0fd93 Mon Sep 17 00:00:00 2001
From: "lcartey@github.com"
Date: Fri, 11 Sep 2020 17:12:10 +0100
Subject: [PATCH 019/185] C++: Support customizable ranges for
RangeSsaDefinitions.
---
.../interfaces/SimpleRangeAnalysisDef.qll | 34 +++++++++++++++++++
.../cpp/rangeanalysis/SimpleRangeAnalysis.qll | 10 ++++++
2 files changed, 44 insertions(+)
create mode 100644 cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll
diff --git a/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll
new file mode 100644
index 00000000000..c2647e2bebc
--- /dev/null
+++ b/cpp/ql/src/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDef.qll
@@ -0,0 +1,34 @@
+/**
+ * EXPERIMENTAL: The API of this module may change without notice.
+ *
+ * Provides a class for modeling `RangeSsaDefinition`s with a restricted range.
+ */
+
+import cpp
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+
+/**
+ * EXPERIMENTAL: The API of this class may change without notice.
+ *
+ * An SSA definition for which a range can be deduced. Extend this class to add
+ * functionality to the range analysis library.
+ */
+abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
+ /**
+ * Gets the lower bound of the defomotopn.
+ *
+ * Implementations of this predicate should use
+ * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
+ * recursive calls to get the bounds of their children.
+ */
+ abstract float getLowerBounds();
+
+ /**
+ * Gets the upper bound of the definition.
+ *
+ * Implementations of this predicate should use
+ * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
+ * recursive calls to get the bounds of their children.
+ */
+ abstract float getUpperBounds();
+}
\ No newline at end of file
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index c8a6e70ce1f..4c61c06c829 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -45,6 +45,7 @@
import cpp
private import RangeAnalysisUtils
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
+private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDef
import RangeSSA
import SimpleRangeAnalysisCached
private import NanAnalysis
@@ -492,6 +493,9 @@ private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) {
v = def.getAVariable()
or
phiDependsOnDef(def, v, _, _)
+ or
+ // A modeled def for range analysis
+ def.(SimpleRangeAnalysisDefinition).getAVariable() =v
}
/**
@@ -1211,6 +1215,9 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Phi nodes.
result = getPhiLowerBounds(v, def)
or
+ // A modeled def for range analysis
+ result = def.(SimpleRangeAnalysisDefinition).getLowerBounds()
+ or
// Unanalyzable definitions.
unanalyzableDefBounds(def, v, result, _)
}
@@ -1244,6 +1251,9 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Phi nodes.
result = getPhiUpperBounds(v, def)
or
+ // A modeled def for range analysis
+ result = def.(SimpleRangeAnalysisDefinition).getUpperBounds()
+ or
// Unanalyzable definitions.
unanalyzableDefBounds(def, v, _, result)
}
From 826fc0a630cb4742875221c31ee3d9ab269b76ca Mon Sep 17 00:00:00 2001
From: Faten Healy <5361987+fatenhealy@users.noreply.github.com>
Date: Sun, 13 Sep 2020 21:04:07 +1000
Subject: [PATCH 020/185] Update BrokenCryptoAlgorithm - Blowfish to AES
---
python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp
index 1b4031b1cc5..ba5ab4d10c1 100644
--- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp
+++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp
@@ -33,7 +33,7 @@
pycrypto you must specify the encryption
algorithm to use. The first example uses DES, which is an
older algorithm that is now considered weak. The second
- example uses Blowfish, which is a stronger more modern algorithm.
+ example uses AES, which is a stronger modern algorithm.
From 6f20516f8433bdab89610c805a360d5b0b9eb3ca Mon Sep 17 00:00:00 2001
From: Faten Healy <5361987+fatenhealy@users.noreply.github.com>
Date: Sun, 13 Sep 2020 21:07:28 +1000
Subject: [PATCH 021/185] Update broken_crypto.py to AES instead of Blowfish
---
python/ql/src/Security/CWE-327/examples/broken_crypto.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/Security/CWE-327/examples/broken_crypto.py b/python/ql/src/Security/CWE-327/examples/broken_crypto.py
index ef9fc75e889..690ed0096b1 100644
--- a/python/ql/src/Security/CWE-327/examples/broken_crypto.py
+++ b/python/ql/src/Security/CWE-327/examples/broken_crypto.py
@@ -6,7 +6,7 @@ def send_encrypted(channel, message):
channel.send(cipher.encrypt(message)) # BAD: weak encryption
-cipher = Blowfish.new(SECRET_KEY)
+cipher = AES.new(SECRET_KEY)
def send_encrypted(channel, message):
channel.send(cipher.encrypt(message)) # GOOD: strong encryption
From e54937756129301eebb6428f97a3bd03eb0929b3 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Mon, 14 Sep 2020 11:00:59 +0200
Subject: [PATCH 022/185] C#: Construct `File::TransformedPathLazy` lazily
This avoids calling the path transformer for `GeneratedFile`s.
---
csharp/extractor/Semmle.Extraction/Entities/File.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs
index a0e9ea6d725..1a5da1cbe1b 100644
--- a/csharp/extractor/Semmle.Extraction/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs
@@ -11,11 +11,12 @@ namespace Semmle.Extraction.Entities
: base(cx, path)
{
OriginalPath = path;
- TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath);
+ TransformedPathLazy = new Lazy(() => Context.Extractor.PathTransformer.Transform(OriginalPath));
}
readonly string OriginalPath;
- readonly PathTransformer.ITransformedPath TransformedPath;
+ readonly Lazy TransformedPathLazy;
+ PathTransformer.ITransformedPath TransformedPath => TransformedPathLazy.Value;
public override bool NeedsPopulation => Context.DefinesFile(OriginalPath) || OriginalPath == Context.Extractor.OutputPath;
From 5f2cafc4f5571f06928b43cbb37d97b5a7044640 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 14 Sep 2020 14:36:19 -0700
Subject: [PATCH 023/185] C++: Interprocedural iterator flow
---
.../code/cpp/dataflow/internal/FlowVar.qll | 43 ++++++++++++++++---
.../dataflow/taint-tests/localTaint.expected | 17 ++++++++
.../dataflow/taint-tests/taint.expected | 1 +
.../dataflow/taint-tests/test_diff.expected | 1 +
4 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index c6929358543..0163d1e0ce7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -109,11 +109,10 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- class PartialDefinition extends Expr {
+ abstract class PartialDefinition extends Expr {
ControlFlowNode node;
PartialDefinition() {
- valueToUpdate(_, this.getFullyConverted(), node) and
not this instanceof Conversion
}
@@ -160,14 +159,39 @@ private module PartialDefinitions {
class IteratorPartialDefinition extends PartialDefinition {
Variable collection;
- Call innerDefinedExpr;
+ Expr innerDefinedExpr;
IteratorPartialDefinition() {
exists(Expr convertedInner |
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
innerDefinedExpr = convertedInner.getUnconverted() and
- innerDefinedExpr.getQualifier() = getAnIteratorAccess(collection) and
- innerDefinedExpr.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ (
+ innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
+ or
+ innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
+ collection instanceof IteratorParameter
+ ) and
+ innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ or
+ // iterators passed by value without a copy constructor
+ exists(Call call |
+ call = node and
+ call.getAnArgument() = innerDefinedExpr and
+ innerDefinedExpr = this and
+ this = getAnIteratorAccess(collection) and
+ not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ or
+ // iterators passed by value with a copy constructor
+ exists(Call call, ConstructorCall copy |
+ copy.getTarget() instanceof CopyConstructor and
+ call = node and
+ call.getAnArgument() = copy and
+ copy.getArgument(0) = getAnIteratorAccess(collection) and
+ innerDefinedExpr = this and
+ this = copy and
+ not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
)
}
@@ -267,7 +291,8 @@ module FlowVar_internal {
// The SSA library has a theoretically accurate treatment of reference types,
// treating them as immutable, but for data flow it gives better results in
// practice to make the variable synonymous with its contents.
- not v.getUnspecifiedType() instanceof ReferenceType
+ not v.getUnspecifiedType() instanceof ReferenceType and
+ not v instanceof IteratorParameter
}
/**
@@ -616,6 +641,8 @@ module FlowVar_internal {
refType = p.getUnderlyingType() and
not refType.getBaseType().isConst()
)
+ or
+ p instanceof IteratorParameter
}
/**
@@ -778,6 +805,10 @@ module FlowVar_internal {
)
}
+ class IteratorParameter extends Parameter {
+ IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
+ }
+
/**
* Holds if `v` is initialized to have value `assignedExpr`.
*/
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 13d38363b35..396fecf212f 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -439,23 +439,29 @@
| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT |
+| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
| standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
| standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | TAINT |
| standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT |
| standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:42:14:42:20 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
| standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | TAINT |
+| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
| standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
| standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | TAINT |
| standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT |
| standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:48:14:48:20 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
| standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | TAINT |
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | |
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | |
@@ -2967,10 +2973,13 @@
| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | |
| vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
| vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | |
+| vector.cpp:255:13:255:14 | ref arg call to iterator | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:255:13:255:14 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | |
| vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
| vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | |
| vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | |
| vector.cpp:258:8:258:9 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | |
+| vector.cpp:259:8:259:9 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | |
| vector.cpp:261:8:261:9 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | |
| vector.cpp:265:22:265:23 | call to vector | vector.cpp:269:3:269:4 | v7 | |
| vector.cpp:265:22:265:23 | call to vector | vector.cpp:273:8:273:9 | v7 | |
@@ -3173,7 +3182,9 @@
| vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
| vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | |
| vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | |
+| vector.cpp:329:62:329:65 | iter | vector.cpp:329:62:329:65 | iter | |
| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | |
+| vector.cpp:330:2:330:2 | call to operator* [post update] | vector.cpp:329:62:329:65 | iter | |
| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | |
| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT |
| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT |
@@ -3279,6 +3290,12 @@
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | |
| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | |
+| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 6759dd90d60..889e1da17e6 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -328,6 +328,7 @@
| vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source |
| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source |
| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source |
+| vector.cpp:353:7:353:8 | v4 | vector.cpp:330:10:330:15 | call to source |
| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source |
| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source |
| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 8fbeb0c246c..0f6b7a574a5 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -277,6 +277,7 @@
| vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only |
| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only |
| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only |
+| vector.cpp:353:7:353:8 | vector.cpp:330:10:330:15 | AST only |
| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only |
| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only |
| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only |
From d728c3948c85b2bf782b38693071e44933f1bf82 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Tue, 15 Sep 2020 09:17:42 +0100
Subject: [PATCH 024/185] JS: Log the amount of memory passed to TypeScript
process
---
.../extractor/src/com/semmle/js/parser/TypeScriptParser.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java
index 2dea7826879..c091e39af55 100644
--- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java
+++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java
@@ -299,6 +299,8 @@ public class TypeScriptParser {
: getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_SUFFIX, 2000);
int reserveMemoryMb = getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_RESERVE_SUFFIX, 400);
+ System.out.println("Memory for TypeScript process: " + mainMemoryMb + " MB, and " + reserveMemoryMb + " MB reserve");
+
File parserWrapper = getParserWrapper();
String debugFlagString = Env.systemEnv().getNonEmpty(TYPESCRIPT_NODE_FLAGS);
From fa0e27b2de6c9fa8d9fbe500b4ba7accda8b0cbe Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 16 Sep 2020 12:34:52 -0700
Subject: [PATCH 025/185] C++: move interprocedural iterator flow to taint
---
.../code/cpp/dataflow/internal/DataFlowUtil.qll | 6 +++---
.../semmle/code/cpp/dataflow/internal/FlowVar.qll | 12 ++++++++----
.../cpp/dataflow/internal/TaintTrackingUtil.qll | 15 ++++++++++-----
.../library-tests/dataflow/taint-tests/vector.cpp | 9 ++++++++-
4 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 9705ca492ce..1103e50ce16 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -303,7 +303,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode {
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
}
-private class IteratorPartialDefinitionNode extends PartialDefinitionNode {
+class IteratorPartialDefinitionNode extends PartialDefinitionNode {
override IteratorPartialDefinition pd;
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
@@ -546,10 +546,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
// from which there is field flow to `x` via reverse read.
- exists(PartialDefinition def, Expr inner, Expr outer |
+ exists(VariablePartialDefinition def, Expr inner, Expr outer |
def.definesExpressions(inner, outer) and
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
- outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
+ outer = nodeFrom.(VariablePartialDefinitionNode).getPreUpdateNode().asExpr()
)
or
// Reverse flow: data that flows from the post-update node of a reference
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 0163d1e0ce7..d4c264184d7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -109,7 +109,7 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- abstract class PartialDefinition extends Expr {
+ class PartialDefinition extends Expr {
ControlFlowNode node;
PartialDefinition() {
@@ -216,8 +216,7 @@ private module PartialDefinitions {
VariablePartialDefinition() {
exists(Expr convertedInner |
valueToUpdate(convertedInner, this.getFullyConverted(), node) and
- innerDefinedExpr = convertedInner.getUnconverted() and
- not this instanceof Conversion
+ innerDefinedExpr = convertedInner.getUnconverted()
)
}
@@ -249,11 +248,16 @@ private module PartialDefinitions {
/**
* A partial definition that's a definition by reference.
*/
- class DefinitionByReference extends PartialDefinition {
+ class DefinitionByReference extends VariablePartialDefinition {
DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
}
+predicate quickTest(PartialDefinition pd) {
+ pd instanceof DefinitionByReference and
+ pd instanceof IteratorPartialDefinition
+}
+
import PartialDefinitions
private import FlowVar_internal
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index c8d963866a1..f52c3cddf7e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -31,6 +31,11 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
*/
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
+ or
+ exists(FunctionCall call, int i |
+ sink.(DataFlow::IteratorPartialDefinitionNode).getPartialDefinition().definesExpressions(_, call.getArgument(i)) and
+ src.(DataFlow::RefParameterFinalValueNode).getParameter() = call.getTarget().getParameter(i)
+ )
}
/**
@@ -258,9 +263,9 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) {
}
private predicate iteratorDereference(Call c) {
- c.getTarget() instanceof IteratorArrayMemberOperator
- or
- c.getTarget() instanceof IteratorPointerDereferenceMemberOperator
- or
- c.getTarget() instanceof IteratorPointerDereferenceOperator
+ c.getTarget() instanceof IteratorArrayMemberOperator
+ or
+ c.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ or
+ c.getTarget() instanceof IteratorPointerDereferenceOperator
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 153385a202d..4fc63acbad2 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -331,7 +331,7 @@ void taint_vector_output_iterator(std::vector::iterator iter) {
}
void test_vector_output_iterator(int b) {
- std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10);
+ std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10);
std::vector::iterator i1 = v1.begin();
*i1 = source();
@@ -379,4 +379,11 @@ void test_vector_output_iterator(int b) {
sink(v8); // tainted [NOT DETECTED by IR]
*i8 = 1;
sink(v8);
+
+ std::vector::iterator i9 = v9.begin();
+
+ *i9 = source();
+ taint_vector_output_iterator(i9);
+
+ sink(v9);
}
From 086d074a26e55d45ff6e7c70a91124eee0c3fac7 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 16 Sep 2020 12:48:38 -0700
Subject: [PATCH 026/185] C++: make PartialDefinition abstract
---
.../src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index d4c264184d7..45a537f8049 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -109,16 +109,16 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- class PartialDefinition extends Expr {
+ abstract class PartialDefinition extends Expr {
ControlFlowNode node;
PartialDefinition() {
not this instanceof Conversion
}
- deprecated predicate partiallyDefines(Variable v) { none() }
+ deprecated abstract predicate partiallyDefines(Variable v);
- deprecated predicate partiallyDefinesThis(ThisExpr e) { none() }
+ deprecated abstract predicate partiallyDefinesThis(ThisExpr e);
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
@@ -131,7 +131,7 @@ private module PartialDefinitions {
*/
// does this work with a dispred?
pragma[noinline]
- predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) { none() }
+ abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn);
/**
* Holds if this partial definition may modify `inner` (or what it points
@@ -141,7 +141,7 @@ private module PartialDefinitions {
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
- predicate definesExpressions(Expr inner, Expr outer) { none() }
+ abstract predicate definesExpressions(Expr inner, Expr outer);
/**
* Gets the location of this element, adjusted to avoid unknown locations
From 44c5233459a67643a405200398fb5dc70c60fea2 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Wed, 16 Sep 2020 12:49:15 -0700
Subject: [PATCH 027/185] C++: accept test output
---
.../dataflow/taint-tests/localTaint.expected | 116 ++++++++++--------
.../dataflow/taint-tests/taint.expected | 2 +
.../dataflow/taint-tests/test_diff.expected | 2 +
3 files changed, 72 insertions(+), 48 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 396fecf212f..e5e4905e395 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -2972,9 +2972,9 @@
| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:261:8:261:9 | v6 | |
| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | |
| vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
+| vector.cpp:255:13:255:14 | call to iterator [post update] | vector.cpp:277:1:277:1 | v3 | |
| vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | |
-| vector.cpp:255:13:255:14 | ref arg call to iterator | vector.cpp:277:1:277:1 | v3 | |
-| vector.cpp:255:13:255:14 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:255:13:255:14 | i1 [post update] | vector.cpp:277:1:277:1 | v3 | |
| vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
| vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | |
| vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | |
@@ -3192,57 +3192,60 @@
| vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | |
| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | |
| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:334:22:334:24 | call to vector | vector.cpp:382:1:382:1 | v1 | |
+| vector.cpp:334:22:334:24 | call to vector | vector.cpp:389:1:389:1 | v1 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:382:1:382:1 | v2 | |
+| vector.cpp:334:30:334:32 | call to vector | vector.cpp:389:1:389:1 | v2 | |
| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | |
| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | |
-| vector.cpp:334:38:334:40 | call to vector | vector.cpp:382:1:382:1 | v3 | |
+| vector.cpp:334:38:334:40 | call to vector | vector.cpp:389:1:389:1 | v3 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:334:46:334:48 | call to vector | vector.cpp:389:1:389:1 | v4 | |
| vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | |
| vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | |
| vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:334:54:334:56 | call to vector | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:334:54:334:56 | call to vector | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | |
| vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | |
| vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | |
| vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:334:62:334:64 | call to vector | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | |
| vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | |
| vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | |
| vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:334:70:334:72 | call to vector | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | |
| vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | |
| vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:334:78:334:80 | call to vector | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:334:78:334:80 | call to vector | vector.cpp:389:1:389:1 | v8 | |
+| vector.cpp:334:86:334:88 | call to vector | vector.cpp:383:34:383:35 | v9 | |
+| vector.cpp:334:86:334:88 | call to vector | vector.cpp:388:7:388:8 | v9 | |
+| vector.cpp:334:86:334:88 | call to vector | vector.cpp:389:1:389:1 | v9 | |
| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | |
+| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | |
| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT |
| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | |
| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v1 | |
+| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v1 | |
| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | |
| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT |
| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT |
| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | |
-| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:382:1:382:1 | v1 | |
+| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
+| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | |
| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | |
| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
+| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT |
| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | TAINT |
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | |
@@ -3250,12 +3253,12 @@
| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | |
| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | |
| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v2 | |
+| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v2 | |
| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | |
| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT |
| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT |
| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | |
-| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:382:1:382:1 | v2 | |
+| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT |
| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | TAINT |
| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | |
@@ -3273,119 +3276,136 @@
| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT |
| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | |
-| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:382:1:382:1 | v3 | |
+| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:389:1:389:1 | v3 | |
| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | |
| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT |
| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | TAINT |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | |
+| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:389:1:389:1 | v4 | |
| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | |
-| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:351:32:351:33 | ref arg call to iterator | vector.cpp:382:1:382:1 | v4 | |
-| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:351:32:351:33 | ref arg it | vector.cpp:382:1:382:1 | v4 | |
-| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:382:1:382:1 | v4 | |
+| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:350:56:350:57 | v4 | |
+| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:353:7:353:8 | v4 | |
+| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:389:1:389:1 | v4 | |
+| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | |
| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT |
| vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | |
| vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | |
| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | |
| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | |
| vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT |
| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT |
| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | |
| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | |
| vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT |
| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT |
| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | |
-| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:382:1:382:1 | v5 | |
+| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | |
| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT |
| vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | |
| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | |
| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | |
| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | |
| vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT |
| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT |
| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | |
| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT |
| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT |
-| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:382:1:382:1 | v6 | |
+| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | |
| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | |
| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT |
| vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | |
| vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | |
| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | |
| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | |
| vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT |
| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT |
| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | |
| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | |
| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | |
| vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT |
| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT |
| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | |
| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
-| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:382:1:382:1 | v7 | |
+| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
+| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | |
| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
| vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT |
| vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | |
| vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | |
| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | |
| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | |
| vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | |
| vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT |
| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT |
| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | |
| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | |
| vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | |
| vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT |
| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT |
| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | |
-| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:382:1:382:1 | v8 | |
+| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
+| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:388:7:388:8 | v9 | |
+| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | |
+| vector.cpp:383:34:383:35 | v9 | vector.cpp:383:37:383:41 | call to begin | TAINT |
+| vector.cpp:383:37:383:41 | call to begin | vector.cpp:385:3:385:4 | i9 | |
+| vector.cpp:383:37:383:41 | call to begin | vector.cpp:386:31:386:32 | i9 | |
+| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:388:7:388:8 | v9 | |
+| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v9 | |
+| vector.cpp:385:2:385:15 | ... = ... | vector.cpp:385:2:385:2 | call to operator* [post update] | |
+| vector.cpp:385:3:385:4 | i9 | vector.cpp:385:2:385:2 | call to operator* | TAINT |
+| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:2 | call to operator* [post update] | TAINT |
+| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:15 | ... = ... | |
+| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:388:7:388:8 | v9 | |
+| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:389:1:389:1 | v9 | |
+| vector.cpp:386:31:386:32 | i9 | vector.cpp:386:31:386:32 | call to iterator | |
+| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:388:7:388:8 | v9 | |
+| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:389:1:389:1 | v9 | |
+| vector.cpp:388:7:388:8 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 889e1da17e6..01ef2e1a494 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -337,3 +337,5 @@
| vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source |
| vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source |
| vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source |
+| vector.cpp:388:7:388:8 | v9 | vector.cpp:330:10:330:15 | call to source |
+| vector.cpp:388:7:388:8 | v9 | vector.cpp:385:8:385:13 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 0f6b7a574a5..173bb28f831 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -286,3 +286,5 @@
| vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only |
| vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only |
| vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only |
+| vector.cpp:388:7:388:8 | vector.cpp:330:10:330:15 | AST only |
+| vector.cpp:388:7:388:8 | vector.cpp:385:8:385:13 | AST only |
From 107e9770da5ebde3e06ba7fefd5952a0154ed105 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Fri, 18 Sep 2020 14:12:33 -0700
Subject: [PATCH 028/185] C++: remove accidentally committed test code
---
cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 5 -----
1 file changed, 5 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 45a537f8049..b9e5eadfcf5 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -253,11 +253,6 @@ private module PartialDefinitions {
}
}
-predicate quickTest(PartialDefinition pd) {
- pd instanceof DefinitionByReference and
- pd instanceof IteratorPartialDefinition
-}
-
import PartialDefinitions
private import FlowVar_internal
From b84bf5e9bb5a3a4562592a558290d476b68f83d3 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Fri, 18 Sep 2020 14:18:38 -0700
Subject: [PATCH 029/185] C++: QLDoc for IteratorPartialDefinitionNode
---
.../src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index faa242aec64..d71b3a4ad62 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -304,6 +304,12 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode {
override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
}
+/**
+ * INTERNAL: do not use.
+ *
+ * A synthetic data flow node used for flow into a collection when an iterator
+ * write occurs in a callee.
+ */
class IteratorPartialDefinitionNode extends PartialDefinitionNode {
override IteratorPartialDefinition pd;
From bd7f5a41d1016b001fa1f8dbf3988fac6d93cfb1 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Fri, 18 Sep 2020 14:19:29 -0700
Subject: [PATCH 030/185] C++: autoformat
---
.../semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 8 +++-----
cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll | 8 +++-----
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index d71b3a4ad62..963f1a17826 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -284,12 +284,10 @@ abstract class PostUpdateNode extends Node {
override Location getLocation() { result = getPreUpdateNode().getLocation() }
}
-private abstract class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
+abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
PartialDefinition pd;
- PartialDefinitionNode() {
- this = TPartialDefinitionNode(pd)
- }
+ PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
override Location getLocation() { result = pd.getActualLocation() }
@@ -306,7 +304,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode {
/**
* INTERNAL: do not use.
- *
+ *
* A synthetic data flow node used for flow into a collection when an iterator
* write occurs in a callee.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index b9e5eadfcf5..13f11f8b275 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -112,13 +112,11 @@ private module PartialDefinitions {
abstract class PartialDefinition extends Expr {
ControlFlowNode node;
- PartialDefinition() {
- not this instanceof Conversion
- }
+ PartialDefinition() { not this instanceof Conversion }
- deprecated abstract predicate partiallyDefines(Variable v);
+ abstract deprecated predicate partiallyDefines(Variable v);
- deprecated abstract predicate partiallyDefinesThis(ThisExpr e);
+ abstract deprecated predicate partiallyDefinesThis(ThisExpr e);
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
From a2d006fe479ed4ca35457fa9c98da17cb2572da8 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 16 Sep 2020 12:55:19 +0200
Subject: [PATCH 031/185] Python: Tests for field flow
---
.../dataflow/fieldflow/dataflow.expected | 3 +
.../dataflow/fieldflow/dataflow.ql | 10 ++++
.../dataflow/fieldflow/examples.py | 50 +++++++++++++++++
.../experimental/dataflow/fieldflow/test.py | 56 +++++++++++++++++++
4 files changed, 119 insertions(+)
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflow.ql
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/examples.py
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/test.py
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
new file mode 100644
index 00000000000..58e3dda0964
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
@@ -0,0 +1,3 @@
+edges
+nodes
+#select
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql
new file mode 100644
index 00000000000..1347c440496
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.ql
@@ -0,0 +1,10 @@
+/**
+ * @kind path-problem
+ */
+
+import experimental.dataflow.testConfig
+import DataFlow::PathGraph
+
+from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
+where config.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, ""
diff --git a/python/ql/test/experimental/dataflow/fieldflow/examples.py b/python/ql/test/experimental/dataflow/fieldflow/examples.py
new file mode 100644
index 00000000000..ed961438822
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/examples.py
@@ -0,0 +1,50 @@
+from python.ql.test.experimental.dataflow.testDefinitions import *
+
+# Preamble
+
+class MyObj(object):
+
+ def __init__(self, foo):
+ self.foo = foo
+
+
+class NestedObj(object):
+
+ def __init__(self):
+ self.obj = MyObj("OK")
+
+ def getObj(self):
+ return self.obj
+
+
+# Example 1
+def setFoo(obj, x):
+ SINK_F(obj.foo)
+ obj.foo = x
+
+myobj = MyObj("OK")
+
+setFoo(myobj, SOURCE)
+SINK(myobj.foo)
+
+# Example 2
+x = SOURCE
+
+a = NestedObj()
+
+a.obj.foo = x
+a.getObj().foo = x
+
+SINK(a.obj.foo)
+
+# Example 3
+obj = MyObj(SOURCE)
+SINK(obj.foo)
+
+# Local flow
+def fields_with_local_flow(x):
+ obj = MyObj(x)
+ a = obj.foo
+ return a
+
+SINK(fields_with_local_flow(SOURCE))
\ No newline at end of file
diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py
new file mode 100644
index 00000000000..a747db14e24
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/test.py
@@ -0,0 +1,56 @@
+from python.ql.test.experimental.dataflow.testDefinitions import *
+
+# Preamble
+
+
+class MyObj(object):
+
+ def __init__(self, foo):
+ self.foo = foo
+
+
+class NestedObj(object):
+
+ def __init__(self):
+ self.obj = MyObj("OK")
+
+ def getObj(self):
+ return self.obj
+
+
+def setFoo(obj, x):
+ SINK_F(obj.foo)
+ obj.foo = x
+
+
+def test_example1():
+ myobj = MyObj("OK")
+
+ setFoo(myobj, SOURCE)
+ SINK(myobj.foo)
+
+
+def test_example2():
+ x = SOURCE
+
+ a = NestedObj()
+
+ a.obj.foo = x
+ a.getObj().foo = x
+
+ SINK(a.obj.foo)
+
+
+def test_example3():
+ obj = MyObj(SOURCE)
+ SINK(obj.foo)
+
+
+def fields_with_local_flow(x):
+ obj = MyObj(x)
+ a = obj.foo
+ return a
+
+
+def test_fields():
+ SINK(fields_with_local_flow(SOURCE))
From 27b25565ca78a99c0565e65b767c8425d4ff28ee Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 16 Sep 2020 13:00:09 +0200
Subject: [PATCH 032/185] Python: Implement field-stores, -reads, and -content
---
.../dataflow/internal/DataFlowPrivate.qll | 46 +++-
.../dataflow/internal/DataFlowPublic.qll | 24 +-
.../dataflow/fieldflow/allLocalFlow.expected | 40 ++++
.../dataflow/fieldflow/allLocalFlow.ql | 8 +
.../dataflow/fieldflow/dataflow.expected | 22 ++
.../dataflow/fieldflow/globalStep.expected | 221 ++++++++++++++++++
.../dataflow/fieldflow/globalStep.ql | 9 +
.../dataflow/fieldflow/localFlow.expected | 12 +
.../dataflow/fieldflow/localFlow.ql | 8 +
.../dataflow/fieldflow/postupdates.expected | 42 ++++
.../dataflow/fieldflow/postupdates.ql | 4 +
11 files changed, 427 insertions(+), 9 deletions(-)
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/globalStep.ql
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/localFlow.ql
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/postupdates.ql
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index a0edce84f51..b999ebb5982 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -361,7 +361,7 @@ class DataFlowType extends TDataFlowType {
}
/** A node that performs a type cast. */
-class CastNode extends Node {
+class CastNode extends CfgNode {
CastNode() { none() }
}
@@ -423,6 +423,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
dictStoreStep(nodeFrom, c, nodeTo)
or
comprehensionStoreStep(nodeFrom, c, nodeTo)
+ or
+ attributeStoreStep(nodeFrom, c, nodeTo)
}
/** Data flows from an element of a list to the list. */
@@ -497,6 +499,30 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
c instanceof ListElementContent
}
+/**
+ * In
+ * ```python
+ * obj.foo = x
+ * ```
+ * data flows from `x` to (the post-update node for) `obj` via assignment to `foo`.
+ */
+predicate attributeStoreStep(CfgNode nodeFrom, Content c, PostUpdateNode nodeTo) {
+ exists(AssignStmt a, Attribute attr |
+ a.getValue().getAFlowNode() = nodeFrom.getNode() and
+ a.getATarget().(Attribute) = attr and
+ attr.getName() = c.(AttributeContent).getAttribute() and
+ attr.getObject().getAFlowNode() = nodeTo.getPreUpdateNode().(CfgNode).getNode() and
+ attr.getCtx() instanceof Store
+ )
+ or
+ exists(AssignExpr ae |
+ ae.getValue().getAFlowNode() = nodeFrom.getNode() and
+ ae.getTarget().(Attribute).getName() = c.(AttributeContent).getAttribute() and
+ ae.getTarget().(Attribute).getObject().getAFlowNode() =
+ nodeTo.getPreUpdateNode().(CfgNode).getNode()
+ )
+}
+
/**
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
*/
@@ -506,6 +532,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
popReadStep(nodeFrom, c, nodeTo)
or
comprehensionReadStep(nodeFrom, c, nodeTo)
+ or
+ attributeReadStep(nodeFrom, c, nodeTo)
}
/** Data flows from a sequence to a subscript of the sequence. */
@@ -592,6 +620,22 @@ predicate comprehensionReadStep(CfgNode nodeFrom, Content c, EssaNode nodeTo) {
)
}
+/**
+ * In
+ * ```python
+ * obj.foo
+ * ```
+ * data flows from `obj` to `obj.foo` via a read from `foo`.
+ */
+predicate attributeReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
+ exists(Attribute attr |
+ nodeTo.asCfgNode().(AttrNode).getNode() = attr and
+ nodeFrom.asCfgNode() = attr.getObject().getAFlowNode() and
+ attr.getName() = c.(AttributeContent).getAttribute() and
+ attr.getCtx() instanceof Load
+ )
+}
+
/**
* Holds if values stored inside content `c` are cleared at node `n`. For example,
* any value stored inside `f` is cleared at the pre-update node associated with `x`
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index 97626b8951e..e9e143ca7ad 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -202,7 +202,9 @@ newtype TContent =
key = any(Keyword kw).getArg()
} or
/** An element of a dictionary at any key. */
- TDictionaryElementAnyContent()
+ TDictionaryElementAnyContent() or
+ /** An object attribute. */
+ TAttributeContent(string attr) { attr = any(Attribute a).getName() }
class Content extends TContent {
/** Gets a textual representation of this element. */
@@ -210,12 +212,10 @@ class Content extends TContent {
}
class ListElementContent extends TListElementContent, Content {
- /** Gets a textual representation of this element. */
override string toString() { result = "List element" }
}
class SetElementContent extends TSetElementContent, Content {
- /** Gets a textual representation of this element. */
override string toString() { result = "Set element" }
}
@@ -224,10 +224,9 @@ class TupleElementContent extends TTupleElementContent, Content {
TupleElementContent() { this = TTupleElementContent(index) }
- /** Gets the index for this tuple element */
+ /** Gets the index for this tuple element. */
int getIndex() { result = index }
- /** Gets a textual representation of this element. */
override string toString() { result = "Tuple element at index " + index.toString() }
}
@@ -236,14 +235,23 @@ class DictionaryElementContent extends TDictionaryElementContent, Content {
DictionaryElementContent() { this = TDictionaryElementContent(key) }
- /** Gets the index for this tuple element */
+ /** Gets the key for this dictionary element. */
string getKey() { result = key }
- /** Gets a textual representation of this element. */
override string toString() { result = "Dictionary element at key " + key }
}
class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content {
- /** Gets a textual representation of this element. */
override string toString() { result = "Any dictionary element" }
}
+
+class AttributeContent extends TAttributeContent, Content {
+ private string attr;
+
+ AttributeContent() { this = TAttributeContent(attr) }
+
+ /** Gets the name of the attribute under which this content is stored. */
+ string getAttribute() { result = attr }
+
+ override string toString() { result = "Attribute " + attr }
+}
diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
new file mode 100644
index 00000000000..ba3032cd991
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
@@ -0,0 +1,40 @@
+| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ |
+| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * |
+| test.py:6:13:6:18 | ControlFlowNode for object | test.py:12:17:12:22 | ControlFlowNode for object |
+| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
+| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
+| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
+| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql
new file mode 100644
index 00000000000..0da43687495
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.ql
@@ -0,0 +1,8 @@
+import python
+import experimental.dataflow.DataFlow
+
+from DataFlow::Node nodeFrom, DataFlow::Node nodeTo
+where
+ DataFlow::localFlowStep(nodeFrom, nodeTo) and
+ nodeFrom.getLocation().getFile().getBaseName() = "test.py"
+select nodeFrom, nodeTo
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
index 58e3dda0964..fa1e4fc055d 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
@@ -1,3 +1,25 @@
edges
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
+| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
nodes
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
+| test.py:30:10:30:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
+| test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
#select
+| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | |
+| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
new file mode 100644
index 00000000000..204b08b85ce
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
@@ -0,0 +1,221 @@
+| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj |
+| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj |
+| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
+| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj |
+| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj |
+| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ |
+| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ |
+| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj |
+| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
+| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
+| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
+| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
+| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo |
+| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo |
+| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F |
+| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F |
+| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] |
+| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] |
+| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 |
+| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 |
+| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj |
+| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj |
+| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK |
+| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK |
+| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
+| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
+| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo |
+| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
+| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
+| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
+| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
+| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 |
+| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 |
+| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj |
+| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj |
+| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK |
+| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK |
+| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE |
+| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE |
+| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute |
+| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
+| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
+| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 |
+| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 |
+| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj |
+| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj |
+| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK |
+| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK |
+| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
+| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
+| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
+| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
+| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
+| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj |
+| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE |
+| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
+| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
+| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields |
+| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields |
+| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK |
+| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK |
+| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
+| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
+| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow |
+| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql
new file mode 100644
index 00000000000..141e0587156
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.ql
@@ -0,0 +1,9 @@
+import experimental.dataflow.basic.allFlowsConfig
+
+from DataFlow::PathNode fromNode, DataFlow::PathNode toNode
+where
+ toNode = fromNode.getASuccessor() and
+ fromNode.getNode().getLocation().getFile().getBaseName() = "test.py" and
+ fromNode.getNode().getLocation().getStartLine() > 1 and
+ not toNode.getNode().(DataFlow::CfgNode).getNode().isNormalExit()
+select fromNode, toNode
diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
new file mode 100644
index 00000000000..93a4d590d01
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
@@ -0,0 +1,12 @@
+| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x |
+| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x |
+| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
+| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
+| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a |
+| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql b/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql
new file mode 100644
index 00000000000..1b413bedeca
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.ql
@@ -0,0 +1,8 @@
+import python
+import experimental.dataflow.DataFlow
+
+from DataFlow::Node nodeFrom, DataFlow::Node nodeTo
+where
+ DataFlow::localFlowStep(nodeFrom, nodeTo) and
+ nodeFrom.getEnclosingCallable().getName().matches("%\\_with\\_local\\_flow")
+select nodeFrom, nodeTo
diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
new file mode 100644
index 00000000000..750ef851ade
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
@@ -0,0 +1,42 @@
+| examples.py:8:9:8:12 | [post] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self |
+| examples.py:14:9:14:12 | [post] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self |
+| examples.py:14:26:14:29 | [post] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str |
+| examples.py:17:16:17:19 | [post] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self |
+| examples.py:22:12:22:14 | [post] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj |
+| examples.py:23:5:23:7 | [post] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj |
+| examples.py:25:15:25:18 | [post] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str |
+| examples.py:27:8:27:12 | [post] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj |
+| examples.py:27:15:27:20 | [post] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE |
+| examples.py:28:6:28:10 | [post] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj |
+| examples.py:35:1:35:1 | [post] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a |
+| examples.py:35:1:35:5 | [post] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute |
+| examples.py:36:1:36:1 | [post] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a |
+| examples.py:36:1:36:10 | [post] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() |
+| examples.py:38:6:38:6 | [post] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a |
+| examples.py:38:6:38:10 | [post] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute |
+| examples.py:41:13:41:18 | [post] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
+| examples.py:42:6:42:8 | [post] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj |
+| examples.py:46:15:46:15 | [post] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x |
+| examples.py:47:7:47:9 | [post] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
+| examples.py:50:29:50:34 | [post] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
+| test.py:9:9:9:12 | [post] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:15:9:15:12 | [post] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:15:26:15:29 | [post] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str |
+| test.py:18:16:18:19 | [post] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self |
+| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:27:19:27:22 | [post] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str |
+| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:29:19:29:24 | [post] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
+| test.py:30:10:30:14 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute |
+| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:39:5:39:14 | [post] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() |
+| test.py:41:10:41:10 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:41:10:41:14 | [post] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute |
+| test.py:45:17:45:22 | [post] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
+| test.py:46:10:46:12 | [post] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:51:9:51:11 | [post] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql b/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql
new file mode 100644
index 00000000000..b034ad9c9be
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.ql
@@ -0,0 +1,4 @@
+import experimental.dataflow.testConfig
+
+from DataFlow::PostUpdateNode pun
+select pun, pun.getPreUpdateNode()
From aa281671774d13f669c3e9d025997b0640d78ec6 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 17 Sep 2020 11:39:17 +0200
Subject: [PATCH 033/185] Python: Add malloc nodes
---
.../dataflow/internal/DataFlowPrivate.qll | 52 +++++-
.../dataflow/fieldflow/allLocalFlow.expected | 20 ++-
.../dataflow/fieldflow/dataflow.expected | 16 +-
.../dataflow/fieldflow/globalStep.expected | 152 +++++++++++-------
.../dataflow/fieldflow/localFlow.expected | 6 +-
.../dataflow/fieldflow/postupdates.expected | 94 ++++++-----
6 files changed, 218 insertions(+), 122 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index b999ebb5982..c2766a3ef95 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -17,10 +17,14 @@ class DataFlowCfgNode extends ControlFlowNode {
}
/** A data flow node which should have an associated post-update node. */
-abstract class PreUpdateNode extends Node { }
+abstract class PreUpdateNode extends Node {
+ abstract string label();
+}
/** An argument might have its value changed as a result of a call. */
-class ArgumentPreUpdateNode extends PreUpdateNode, ArgumentNode { }
+class ArgumentPreUpdateNode extends PreUpdateNode, ExplicitArgumentNode {
+ override string label() { result = "arg" }
+}
/** An object might have its value changed after a store. */
class StorePreUpdateNode extends PreUpdateNode, CfgNode {
@@ -30,6 +34,8 @@ class StorePreUpdateNode extends PreUpdateNode, CfgNode {
a.getCtx() instanceof Store
)
}
+
+ override string label() { result = "store" }
}
/** A node marking the state change of an object after a read. */
@@ -40,6 +46,17 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
a.getCtx() instanceof Load
)
}
+
+ override string label() { result = "read" }
+}
+
+class MallocNode extends PreUpdateNode, ImplicitSelfArgumentNode {
+ // ObjectCreationNode() { exists(ClassValue c | this.asCfgNode() = c.getACall()) }
+ override string toString() {
+ result = "malloc " + this.asCfgNode().(CallNode).getNode().(Call).toString()
+ }
+
+ override string label() { result = "malloc" }
}
/**
@@ -61,7 +78,7 @@ class PostUpdateNode extends Node, TPostUpdateNode {
/** Gets the node before the state update. */
Node getPreUpdateNode() { result = pre }
- override string toString() { result = "[post] " + pre.toString() }
+ override string toString() { result = "[post " + pre.label() + "] " + pre.toString() }
override Scope getScope() { result = pre.getScope() }
@@ -297,14 +314,33 @@ class SpecialCall extends DataFlowCall, TSpecialCall {
}
/** A data flow node that represents a call argument. */
-class ArgumentNode extends CfgNode {
- ArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) }
-
+abstract class ArgumentNode extends CfgNode {
/** Holds if this argument occurs at the given position in the given call. */
- predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) }
+ abstract predicate argumentOf(DataFlowCall call, int pos);
/** Gets the call in which this node is an argument. */
- final DataFlowCall getCall() { this.argumentOf(result, _) }
+ abstract DataFlowCall getCall();
+}
+
+/** A data flow node that represents a call argument. */
+class ExplicitArgumentNode extends ArgumentNode {
+ ExplicitArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) }
+
+ /** Holds if this argument occurs at the given position in the given call. */
+ override predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) }
+
+ /** Gets the call in which this node is an argument. */
+ final override DataFlowCall getCall() { this.argumentOf(result, _) }
+}
+
+class ImplicitSelfArgumentNode extends ArgumentNode {
+ ImplicitSelfArgumentNode() { exists(ClassValue cv | node = cv.getACall()) }
+
+ /** Holds if this argument occurs at the given position in the given call. */
+ override predicate argumentOf(DataFlowCall call, int pos) { call = TCallNode(node) and pos = -1 }
+
+ /** Gets the call in which this node is an argument. */
+ final override DataFlowCall getCall() { result = TCallNode(node) }
}
/** Gets a viable run-time target for the call `call`. */
diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
index ba3032cd991..1e36d80a5ee 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
@@ -14,27 +14,31 @@
| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
index fa1e4fc055d..47ab357dc2c 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
@@ -1,21 +1,21 @@
edges
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
-| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
nodes
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
| test.py:30:10:30:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:34:9:34:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] |
| test.py:38:17:38:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
index 204b08b85ce..0fe90c99f8f 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
@@ -4,6 +4,10 @@
| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
+| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self |
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
@@ -18,8 +22,14 @@
| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
+| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
@@ -50,15 +60,15 @@
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
-| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
-| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:29:12:29:16 | [post] ControlFlowNode for myobj |
-| test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
-| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] |
-| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post] ControlFlowNode for obj [Attribute foo] |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj |
+| test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
+| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] |
+| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] |
| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 |
| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 |
| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj |
@@ -75,26 +85,36 @@
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x |
-| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post] ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 |
@@ -123,39 +143,51 @@
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
-| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj] |
-| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post] ControlFlowNode for a [Attribute obj, Attribute foo] |
-| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
-| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute |
@@ -171,10 +203,16 @@
| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
@@ -191,16 +229,22 @@
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
-| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE |
-| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE |
+| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
+| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
index 93a4d590d01..6651727c229 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
@@ -1,12 +1,14 @@
| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x |
| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x |
| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
-| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
+| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj |
+| examples.py:46:9:46:16 | malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj |
| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a |
| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
index 750ef851ade..4bbb7f3542b 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
@@ -1,42 +1,52 @@
-| examples.py:8:9:8:12 | [post] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self |
-| examples.py:14:9:14:12 | [post] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self |
-| examples.py:14:26:14:29 | [post] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str |
-| examples.py:17:16:17:19 | [post] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self |
-| examples.py:22:12:22:14 | [post] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj |
-| examples.py:23:5:23:7 | [post] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj |
-| examples.py:25:15:25:18 | [post] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str |
-| examples.py:27:8:27:12 | [post] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj |
-| examples.py:27:15:27:20 | [post] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE |
-| examples.py:28:6:28:10 | [post] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj |
-| examples.py:35:1:35:1 | [post] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a |
-| examples.py:35:1:35:5 | [post] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute |
-| examples.py:36:1:36:1 | [post] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a |
-| examples.py:36:1:36:10 | [post] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() |
-| examples.py:38:6:38:6 | [post] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a |
-| examples.py:38:6:38:10 | [post] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute |
-| examples.py:41:13:41:18 | [post] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
-| examples.py:42:6:42:8 | [post] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj |
-| examples.py:46:15:46:15 | [post] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x |
-| examples.py:47:7:47:9 | [post] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
-| examples.py:50:29:50:34 | [post] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
-| test.py:9:9:9:12 | [post] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self |
-| test.py:15:9:15:12 | [post] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self |
-| test.py:15:26:15:29 | [post] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str |
-| test.py:18:16:18:19 | [post] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self |
-| test.py:22:12:22:14 | [post] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj |
-| test.py:23:5:23:7 | [post] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:27:19:27:22 | [post] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str |
-| test.py:29:12:29:16 | [post] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:29:19:29:24 | [post] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
-| test.py:30:10:30:14 | [post] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:38:5:38:5 | [post] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:38:5:38:9 | [post] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute |
-| test.py:39:5:39:5 | [post] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:39:5:39:14 | [post] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() |
-| test.py:41:10:41:10 | [post] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:41:10:41:14 | [post] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute |
-| test.py:45:17:45:22 | [post] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
-| test.py:46:10:46:12 | [post] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:50:17:50:17 | [post] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x |
-| test.py:51:9:51:11 | [post] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:56:33:56:38 | [post] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
+| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self |
+| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self |
+| examples.py:14:20:14:30 | [post malloc] malloc MyObj() | examples.py:14:20:14:30 | malloc MyObj() |
+| examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str |
+| examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self |
+| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj |
+| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj |
+| examples.py:25:9:25:19 | [post malloc] malloc MyObj() | examples.py:25:9:25:19 | malloc MyObj() |
+| examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str |
+| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj |
+| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE |
+| examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj |
+| examples.py:33:5:33:15 | [post malloc] malloc NestedObj() | examples.py:33:5:33:15 | malloc NestedObj() |
+| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a |
+| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute |
+| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a |
+| examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() |
+| examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a |
+| examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute |
+| examples.py:41:7:41:19 | [post malloc] malloc MyObj() | examples.py:41:7:41:19 | malloc MyObj() |
+| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
+| examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj |
+| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:9:46:16 | malloc MyObj() |
+| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x |
+| examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
+| examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self |
+| test.py:15:20:15:30 | [post malloc] malloc MyObj() | test.py:15:20:15:30 | malloc MyObj() |
+| test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str |
+| test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self |
+| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj |
+| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:13:27:23 | malloc MyObj() |
+| test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
+| test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:9:36:19 | malloc NestedObj() |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute |
+| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() |
+| test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:11:45:23 | malloc MyObj() |
+| test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
+| test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:11:50:18 | malloc MyObj() |
+| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
From e50b66554dfeba72c91b15cd58247ce133902022 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 17 Sep 2020 16:30:05 +0200
Subject: [PATCH 034/185] Python: Add explorative test
---
.../dataflow/fieldflow/dataflowExplore.ql | 13 +++++++++++++
python/ql/test/experimental/dataflow/testConfig.qll | 2 ++
2 files changed, 15 insertions(+)
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql
new file mode 100644
index 00000000000..393e14a0b3c
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.ql
@@ -0,0 +1,13 @@
+/**
+ * @kind path-problem
+ */
+
+import experimental.dataflow.testConfig
+import DataFlow::PartialPathGraph
+
+from TestConfiguration config, DataFlow::PartialPathNode source, DataFlow::PartialPathNode sink
+where
+ config.hasPartialFlow(source, sink, _) and
+ source.hasLocationInfo(_, 45, _, _, _) and
+ config.isSink(sink.getNode())
+select sink.getNode(), source, sink, ""
diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll
index f2e35ab9d1c..518ac2b59fc 100644
--- a/python/ql/test/experimental/dataflow/testConfig.qll
+++ b/python/ql/test/experimental/dataflow/testConfig.qll
@@ -43,4 +43,6 @@ class TestConfiguration extends DataFlow::Configuration {
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
+
+ override int explorationLimit() { result = 4 }
}
From e1323617363b902f3647e9457a4d04209e12338e Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 17 Sep 2020 17:18:01 +0200
Subject: [PATCH 035/185] Python: Add missing .expected file
---
.../fieldflow/dataflowExplore.expected | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
new file mode 100644
index 00000000000..d68142f928f
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
@@ -0,0 +1,43 @@
+edges
+| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE |
+| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
+| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
+| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] |
+| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
+| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] |
+| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x |
+| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
+| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
+| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
+| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
+| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] |
+| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, ... (2)] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
+| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
+| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] |
+| test.py:39:22:39:22 | ControlFlowNode for x | test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] |
+| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
+| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
+| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE [Attribute foo] |
+| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
+| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
+| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:17:50:17 | [post arg] ControlFlowNode for x [Attribute foo] |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE [Attribute foo] |
+#select
+| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | |
From b2f1c435a864bf9ae8a3975b89d7fb215a890d6d Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 18 Sep 2020 09:50:00 +0200
Subject: [PATCH 036/185] Python: update test expectations
---
.../dataflow/basic/local.expected | 2 +-
.../dataflow/basic/sinks.expected | 2 +-
.../dataflow/basic/sources.expected | 2 +-
.../coverage/classesCallGraph.expected | 169 +++++++++++++++++-
4 files changed, 168 insertions(+), 7 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/basic/local.expected b/python/ql/test/experimental/dataflow/basic/local.expected
index 4bb6b8d6f5c..de8f6b6075e 100644
--- a/python/ql/test/experimental/dataflow/basic/local.expected
+++ b/python/ql/test/experimental/dataflow/basic/local.expected
@@ -38,4 +38,4 @@
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() | test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:7:5:7:20 | GSSA Variable a | test.py:7:5:7:20 | GSSA Variable a |
| test.py:7:19:7:19 | ControlFlowNode for a | test.py:7:19:7:19 | ControlFlowNode for a |
-| test.py:7:19:7:19 | [post] ControlFlowNode for a | test.py:7:19:7:19 | [post] ControlFlowNode for a |
+| test.py:7:19:7:19 | [post arg] ControlFlowNode for a | test.py:7:19:7:19 | [post arg] ControlFlowNode for a |
diff --git a/python/ql/test/experimental/dataflow/basic/sinks.expected b/python/ql/test/experimental/dataflow/basic/sinks.expected
index 5b58a0f844f..bfebc2ef31e 100644
--- a/python/ql/test/experimental/dataflow/basic/sinks.expected
+++ b/python/ql/test/experimental/dataflow/basic/sinks.expected
@@ -23,4 +23,4 @@
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:7:5:7:20 | GSSA Variable a |
| test.py:7:19:7:19 | ControlFlowNode for a |
-| test.py:7:19:7:19 | [post] ControlFlowNode for a |
+| test.py:7:19:7:19 | [post arg] ControlFlowNode for a |
diff --git a/python/ql/test/experimental/dataflow/basic/sources.expected b/python/ql/test/experimental/dataflow/basic/sources.expected
index 5b58a0f844f..bfebc2ef31e 100644
--- a/python/ql/test/experimental/dataflow/basic/sources.expected
+++ b/python/ql/test/experimental/dataflow/basic/sources.expected
@@ -23,4 +23,4 @@
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
| test.py:7:5:7:20 | GSSA Variable a |
| test.py:7:19:7:19 | ControlFlowNode for a |
-| test.py:7:19:7:19 | [post] ControlFlowNode for a |
+| test.py:7:19:7:19 | [post arg] ControlFlowNode for a |
diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
index 1276083cecc..650f271bd27 100644
--- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
+++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
@@ -1,39 +1,200 @@
+| classes.py:41:16:41:22 | malloc super() | classes.py:41:16:41:22 | malloc super() |
| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() |
-| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() |
-| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() |
-| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() |
-| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() |
+| classes.py:45:16:45:25 | malloc With_new() | classes.py:45:16:45:25 | malloc With_new() |
+| classes.py:58:17:58:27 | malloc With_init() | classes.py:52:18:52:21 | SSA variable self |
+| classes.py:58:17:58:27 | malloc With_init() | classes.py:58:17:58:27 | malloc With_init() |
+| classes.py:71:16:71:25 | malloc With_del() | classes.py:71:16:71:25 | malloc With_del() |
+| classes.py:86:17:86:27 | malloc With_repr() | classes.py:86:17:86:27 | malloc With_repr() |
+| classes.py:101:16:101:25 | malloc With_str() | classes.py:101:16:101:25 | malloc With_str() |
+| classes.py:102:5:102:17 | malloc str() | classes.py:102:5:102:17 | malloc str() |
+| classes.py:116:18:116:29 | malloc With_bytes() | classes.py:116:18:116:29 | malloc With_bytes() |
+| classes.py:117:5:117:21 | malloc bytes() | classes.py:117:5:117:21 | malloc bytes() |
+| classes.py:132:19:132:31 | malloc With_format() | classes.py:132:19:132:31 | malloc With_format() |
+| classes.py:138:19:138:31 | malloc With_format() | classes.py:138:19:138:31 | malloc With_format() |
+| classes.py:143:19:143:31 | malloc With_format() | classes.py:143:19:143:31 | malloc With_format() |
+| classes.py:159:15:159:23 | malloc With_lt() | classes.py:159:15:159:23 | malloc With_lt() |
+| classes.py:176:15:176:23 | malloc With_le() | classes.py:176:15:176:23 | malloc With_le() |
+| classes.py:193:15:193:23 | malloc With_eq() | classes.py:193:15:193:23 | malloc With_eq() |
+| classes.py:209:15:209:23 | malloc With_ne() | classes.py:209:15:209:23 | malloc With_ne() |
+| classes.py:225:15:225:23 | malloc With_gt() | classes.py:225:15:225:23 | malloc With_gt() |
+| classes.py:242:15:242:23 | malloc With_ge() | classes.py:242:15:242:23 | malloc With_ge() |
+| classes.py:258:17:258:27 | malloc With_hash() | classes.py:258:17:258:27 | malloc With_hash() |
+| classes.py:263:17:263:27 | malloc With_hash() | classes.py:263:17:263:27 | malloc With_hash() |
+| classes.py:264:9:264:24 | malloc set() | classes.py:264:9:264:24 | malloc set() |
+| classes.py:268:17:268:27 | malloc With_hash() | classes.py:268:17:268:27 | malloc With_hash() |
+| classes.py:269:9:269:30 | malloc frozenset() | classes.py:269:9:269:30 | malloc frozenset() |
+| classes.py:273:17:273:27 | malloc With_hash() | classes.py:273:17:273:27 | malloc With_hash() |
+| classes.py:274:9:274:28 | malloc dict() | classes.py:274:9:274:28 | malloc dict() |
+| classes.py:288:17:288:27 | malloc With_bool() | classes.py:288:17:288:27 | malloc With_bool() |
+| classes.py:289:5:289:19 | malloc bool() | classes.py:289:5:289:19 | malloc bool() |
+| classes.py:293:17:293:27 | malloc With_bool() | classes.py:293:17:293:27 | malloc With_bool() |
+| classes.py:311:20:311:33 | malloc With_getattr() | classes.py:311:20:311:33 | malloc With_getattr() |
+| classes.py:327:25:327:43 | malloc With_getattribute() | classes.py:327:25:327:43 | malloc With_getattribute() |
+| classes.py:343:20:343:33 | malloc With_setattr() | classes.py:343:20:343:33 | malloc With_setattr() |
+| classes.py:359:20:359:33 | malloc With_delattr() | classes.py:359:20:359:33 | malloc With_delattr() |
+| classes.py:374:16:374:25 | malloc With_dir() | classes.py:374:16:374:25 | malloc With_dir() |
+| classes.py:399:16:399:25 | malloc With_get() | classes.py:399:16:399:25 | malloc With_get() |
+| classes.py:401:12:401:17 | malloc arg3() | classes.py:401:12:401:17 | malloc arg3() |
+| classes.py:417:16:417:25 | malloc With_set() | classes.py:417:16:417:25 | malloc With_set() |
+| classes.py:419:12:419:18 | malloc Owner() | classes.py:419:12:419:18 | malloc Owner() |
+| classes.py:435:19:435:31 | malloc With_delete() | classes.py:435:19:435:31 | malloc With_delete() |
+| classes.py:437:12:437:18 | malloc Owner() | classes.py:437:12:437:18 | malloc Owner() |
+| classes.py:453:21:453:35 | malloc With_set_name() | classes.py:453:21:453:35 | malloc With_set_name() |
+| classes.py:454:5:454:53 | malloc type() | classes.py:454:5:454:53 | malloc type() |
+| classes.py:454:29:454:52 | malloc dict() | classes.py:454:29:454:52 | malloc dict() |
+| classes.py:473:5:473:47 | malloc type() | classes.py:473:5:473:47 | malloc type() |
+| classes.py:513:26:513:45 | malloc With_instancecheck() | classes.py:513:26:513:45 | malloc With_instancecheck() |
+| classes.py:530:26:530:45 | malloc With_subclasscheck() | classes.py:530:26:530:45 | malloc With_subclasscheck() |
+| classes.py:548:26:548:51 | malloc Subscript() | classes.py:548:26:548:51 | malloc Subscript() |
+| classes.py:561:17:561:27 | malloc With_call() | classes.py:561:17:561:27 | malloc With_call() |
+| classes.py:577:16:577:25 | malloc With_len() | classes.py:577:16:577:25 | malloc With_len() |
+| classes.py:582:16:582:25 | malloc With_len() | classes.py:582:16:582:25 | malloc With_len() |
+| classes.py:583:5:583:18 | malloc bool() | classes.py:583:5:583:18 | malloc bool() |
+| classes.py:587:16:587:25 | malloc With_len() | classes.py:587:16:587:25 | malloc With_len() |
+| classes.py:604:24:604:41 | malloc With_length_hint() | classes.py:604:24:604:41 | malloc With_length_hint() |
+| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:612:21:612:24 | SSA variable self |
+| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:620:20:620:33 | malloc With_getitem() |
| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self |
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key |
+| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:629:21:629:24 | SSA variable self |
+| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:637:20:637:33 | malloc With_setitem() |
| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self |
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key |
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value |
+| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:647:21:647:24 | SSA variable self |
+| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:654:20:654:33 | malloc With_delitem() |
| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self |
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key |
+| classes.py:671:20:671:33 | malloc With_missing() | classes.py:671:20:671:33 | malloc With_missing() |
| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() |
+| classes.py:687:17:687:27 | malloc With_iter() | classes.py:687:17:687:27 | malloc With_iter() |
+| classes.py:702:21:702:35 | malloc With_reversed() | classes.py:702:21:702:35 | malloc With_reversed() |
+| classes.py:703:5:703:27 | malloc reversed() | classes.py:703:5:703:27 | malloc reversed() |
+| classes.py:718:21:718:35 | malloc With_contains() | classes.py:718:21:718:35 | malloc With_contains() |
+| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:17:727:20 | SSA variable self |
+| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:23:727:27 | SSA variable other |
+| classes.py:735:16:735:25 | malloc With_add() | classes.py:735:16:735:25 | malloc With_add() |
| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self |
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other |
+| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:17:744:20 | SSA variable self |
+| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:23:744:27 | SSA variable other |
+| classes.py:752:16:752:25 | malloc With_sub() | classes.py:752:16:752:25 | malloc With_sub() |
| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self |
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other |
+| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:17:761:20 | SSA variable self |
+| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:23:761:27 | SSA variable other |
+| classes.py:769:16:769:25 | malloc With_mul() | classes.py:769:16:769:25 | malloc With_mul() |
| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self |
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other |
+| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:20:778:23 | SSA variable self |
+| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:26:778:30 | SSA variable other |
+| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:786:19:786:31 | malloc With_matmul() |
| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self |
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other |
+| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:21:795:24 | SSA variable self |
+| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:27:795:31 | SSA variable other |
+| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:803:20:803:33 | malloc With_truediv() |
| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self |
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other |
+| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:22:812:25 | SSA variable self |
+| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:28:812:32 | SSA variable other |
+| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:820:21:820:35 | malloc With_floordiv() |
| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self |
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other |
+| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:17:829:20 | SSA variable self |
+| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:23:829:27 | SSA variable other |
+| classes.py:837:16:837:25 | malloc With_mod() | classes.py:837:16:837:25 | malloc With_mod() |
| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self |
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other |
+| classes.py:854:19:854:31 | malloc With_divmod() | classes.py:854:19:854:31 | malloc With_divmod() |
+| classes.py:871:16:871:25 | malloc With_pow() | classes.py:871:16:871:25 | malloc With_pow() |
+| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:17:863:20 | SSA variable self |
+| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:23:863:27 | SSA variable other |
+| classes.py:877:16:877:25 | malloc With_pow() | classes.py:877:16:877:25 | malloc With_pow() |
| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self |
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other |
+| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:20:886:23 | SSA variable self |
+| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:26:886:30 | SSA variable other |
+| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:894:19:894:31 | malloc With_lshift() |
| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self |
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other |
+| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:20:903:23 | SSA variable self |
+| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:26:903:30 | SSA variable other |
+| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:911:19:911:31 | malloc With_rshift() |
| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self |
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other |
+| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:17:920:20 | SSA variable self |
+| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:23:920:27 | SSA variable other |
+| classes.py:928:16:928:25 | malloc With_and() | classes.py:928:16:928:25 | malloc With_and() |
| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self |
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other |
+| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:17:937:20 | SSA variable self |
+| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:23:937:27 | SSA variable other |
+| classes.py:945:16:945:25 | malloc With_xor() | classes.py:945:16:945:25 | malloc With_xor() |
| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self |
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other |
+| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:16:954:19 | SSA variable self |
+| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:22:954:26 | SSA variable other |
+| classes.py:962:15:962:23 | malloc With_or() | classes.py:962:15:962:23 | malloc With_or() |
| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self |
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other |
+| classes.py:979:17:979:27 | malloc With_radd() | classes.py:979:17:979:27 | malloc With_radd() |
+| classes.py:996:17:996:27 | malloc With_rsub() | classes.py:996:17:996:27 | malloc With_rsub() |
+| classes.py:1013:17:1013:27 | malloc With_rmul() | classes.py:1013:17:1013:27 | malloc With_rmul() |
+| classes.py:1030:20:1030:33 | malloc With_rmatmul() | classes.py:1030:20:1030:33 | malloc With_rmatmul() |
+| classes.py:1047:21:1047:35 | malloc With_rtruediv() | classes.py:1047:21:1047:35 | malloc With_rtruediv() |
+| classes.py:1064:22:1064:37 | malloc With_rfloordiv() | classes.py:1064:22:1064:37 | malloc With_rfloordiv() |
+| classes.py:1081:17:1081:27 | malloc With_rmod() | classes.py:1081:17:1081:27 | malloc With_rmod() |
+| classes.py:1098:20:1098:33 | malloc With_rdivmod() | classes.py:1098:20:1098:33 | malloc With_rdivmod() |
+| classes.py:1115:17:1115:27 | malloc With_rpow() | classes.py:1115:17:1115:27 | malloc With_rpow() |
+| classes.py:1121:17:1121:27 | malloc With_rpow() | classes.py:1121:17:1121:27 | malloc With_rpow() |
+| classes.py:1138:20:1138:33 | malloc With_rlshift() | classes.py:1138:20:1138:33 | malloc With_rlshift() |
+| classes.py:1155:20:1155:33 | malloc With_rrshift() | classes.py:1155:20:1155:33 | malloc With_rrshift() |
+| classes.py:1172:17:1172:27 | malloc With_rand() | classes.py:1172:17:1172:27 | malloc With_rand() |
+| classes.py:1189:17:1189:27 | malloc With_rxor() | classes.py:1189:17:1189:27 | malloc With_rxor() |
+| classes.py:1206:16:1206:25 | malloc With_ror() | classes.py:1206:16:1206:25 | malloc With_ror() |
+| classes.py:1223:17:1223:27 | malloc With_iadd() | classes.py:1223:17:1223:27 | malloc With_iadd() |
+| classes.py:1240:17:1240:27 | malloc With_isub() | classes.py:1240:17:1240:27 | malloc With_isub() |
+| classes.py:1257:17:1257:27 | malloc With_imul() | classes.py:1257:17:1257:27 | malloc With_imul() |
+| classes.py:1274:20:1274:33 | malloc With_imatmul() | classes.py:1274:20:1274:33 | malloc With_imatmul() |
+| classes.py:1291:21:1291:35 | malloc With_itruediv() | classes.py:1291:21:1291:35 | malloc With_itruediv() |
+| classes.py:1308:22:1308:37 | malloc With_ifloordiv() | classes.py:1308:22:1308:37 | malloc With_ifloordiv() |
+| classes.py:1325:17:1325:27 | malloc With_imod() | classes.py:1325:17:1325:27 | malloc With_imod() |
+| classes.py:1342:17:1342:27 | malloc With_ipow() | classes.py:1342:17:1342:27 | malloc With_ipow() |
+| classes.py:1359:20:1359:33 | malloc With_ilshift() | classes.py:1359:20:1359:33 | malloc With_ilshift() |
+| classes.py:1376:20:1376:33 | malloc With_irshift() | classes.py:1376:20:1376:33 | malloc With_irshift() |
+| classes.py:1393:17:1393:27 | malloc With_iand() | classes.py:1393:17:1393:27 | malloc With_iand() |
+| classes.py:1410:17:1410:27 | malloc With_ixor() | classes.py:1410:17:1410:27 | malloc With_ixor() |
+| classes.py:1427:16:1427:25 | malloc With_ior() | classes.py:1427:16:1427:25 | malloc With_ior() |
+| classes.py:1443:16:1443:25 | malloc With_neg() | classes.py:1443:16:1443:25 | malloc With_neg() |
+| classes.py:1458:16:1458:25 | malloc With_pos() | classes.py:1458:16:1458:25 | malloc With_pos() |
+| classes.py:1473:16:1473:25 | malloc With_abs() | classes.py:1473:16:1473:25 | malloc With_abs() |
+| classes.py:1488:19:1488:31 | malloc With_invert() | classes.py:1488:19:1488:31 | malloc With_invert() |
+| classes.py:1503:20:1503:33 | malloc With_complex() | classes.py:1503:20:1503:33 | malloc With_complex() |
+| classes.py:1504:5:1504:25 | malloc complex() | classes.py:1504:5:1504:25 | malloc complex() |
+| classes.py:1518:16:1518:25 | malloc With_int() | classes.py:1518:16:1518:25 | malloc With_int() |
+| classes.py:1519:5:1519:17 | malloc int() | classes.py:1519:5:1519:17 | malloc int() |
+| classes.py:1533:18:1533:29 | malloc With_float() | classes.py:1533:18:1533:29 | malloc With_float() |
+| classes.py:1534:5:1534:21 | malloc float() | classes.py:1534:5:1534:21 | malloc float() |
+| classes.py:1549:18:1549:29 | malloc With_index() | classes.py:1549:18:1549:29 | malloc With_index() |
+| classes.py:1554:18:1554:29 | malloc With_index() | classes.py:1554:18:1554:29 | malloc With_index() |
+| classes.py:1559:18:1559:29 | malloc With_index() | classes.py:1559:18:1559:29 | malloc With_index() |
+| classes.py:1564:18:1564:29 | malloc With_index() | classes.py:1564:18:1564:29 | malloc With_index() |
+| classes.py:1569:18:1569:29 | malloc With_index() | classes.py:1569:18:1569:29 | malloc With_index() |
+| classes.py:1574:18:1574:29 | malloc With_index() | classes.py:1574:18:1574:29 | malloc With_index() |
+| classes.py:1575:5:1575:19 | malloc int() | classes.py:1575:5:1575:19 | malloc int() |
+| classes.py:1579:18:1579:29 | malloc With_index() | classes.py:1579:18:1579:29 | malloc With_index() |
+| classes.py:1580:5:1580:21 | malloc float() | classes.py:1580:5:1580:21 | malloc float() |
+| classes.py:1584:18:1584:29 | malloc With_index() | classes.py:1584:18:1584:29 | malloc With_index() |
+| classes.py:1585:5:1585:23 | malloc complex() | classes.py:1585:5:1585:23 | malloc complex() |
+| classes.py:1599:18:1599:29 | malloc With_round() | classes.py:1599:18:1599:29 | malloc With_round() |
+| classes.py:1614:18:1614:29 | malloc With_trunc() | classes.py:1614:18:1614:29 | malloc With_trunc() |
+| classes.py:1630:18:1630:29 | malloc With_floor() | classes.py:1630:18:1630:29 | malloc With_floor() |
+| classes.py:1646:17:1646:27 | malloc With_ceil() | classes.py:1646:17:1646:27 | malloc With_ceil() |
+| classes.py:1665:10:1665:21 | malloc With_enter() | classes.py:1665:10:1665:21 | malloc With_enter() |
+| classes.py:1686:10:1686:20 | malloc With_exit() | classes.py:1686:10:1686:20 | malloc With_exit() |
+| classes.py:1703:18:1703:29 | malloc With_await() | classes.py:1703:18:1703:29 | malloc With_await() |
+| classes.py:1726:18:1726:29 | malloc With_aiter() | classes.py:1726:18:1726:29 | malloc With_aiter() |
+| classes.py:1745:18:1745:29 | malloc With_anext() | classes.py:1745:18:1745:29 | malloc With_anext() |
+| classes.py:1763:19:1763:31 | malloc With_aenter() | classes.py:1763:19:1763:31 | malloc With_aenter() |
+| classes.py:1784:18:1784:29 | malloc With_aexit() | classes.py:1784:18:1784:29 | malloc With_aexit() |
From 9aa0cfb35cd6e5455a321c3b67b7e8cca6a36ddd Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Fri, 18 Sep 2020 22:11:21 +0200
Subject: [PATCH 037/185] Python: class callable -> class call Only have one
type of callable, but have an extra type of call. A constructor call directs
to an init callable (should also handle `call` overrides at some point).
---
.../dataflow/internal/DataFlowPrivate.qll | 115 ++----
.../dataflow/coverage/argumentRouting1.ql | 2 +-
.../coverage/classesCallGraph.expected | 330 +++++++++---------
.../dataflow/fieldflow/allLocalFlow.expected | 16 +-
.../dataflow/fieldflow/dataflow.expected | 16 +
.../fieldflow/dataflowExplore.expected | 11 +-
.../dataflow/fieldflow/globalStep.expected | 195 +++++++----
.../dataflow/fieldflow/localFlow.expected | 8 +-
.../dataflow/fieldflow/postupdates.expected | 20 +-
9 files changed, 370 insertions(+), 343 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index c2766a3ef95..b598c25a5e3 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -50,15 +50,6 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
override string label() { result = "read" }
}
-class MallocNode extends PreUpdateNode, ImplicitSelfArgumentNode {
- // ObjectCreationNode() { exists(ClassValue c | this.asCfgNode() = c.getACall()) }
- override string toString() {
- result = "malloc " + this.asCfgNode().(CallNode).getNode().(Call).toString()
- }
-
- override string label() { result = "malloc" }
-}
-
/**
* A node associated with an object after an operation that might have
* changed its state.
@@ -191,67 +182,13 @@ private Node update(Node node) {
// Global flow
//--------
/**
- * IPA type for DataFlowCallable.
- * A callable is either a callable value or a class.
+ * A DataFlowCallable is any callable value.
*/
-newtype TDataFlowCallable =
- TCallableValue(CallableValue callable) or
- TClassValue(ClassValue c)
-
-/** Represents a callable */
-abstract class DataFlowCallable extends TDataFlowCallable {
- /** Gets a textual representation of this element. */
- abstract string toString();
-
- /** Gets a call to this callable. */
- abstract CallNode getACall();
-
- /** Gets the scope of this callable */
- abstract Scope getScope();
-
- /** Gets the specified parameter of this callable */
- abstract NameNode getParameter(int n);
-
- /** Gets the name of this callable. */
- abstract string getName();
-}
-
-class DataFlowCallableValue extends DataFlowCallable, TCallableValue {
- CallableValue callable;
-
- DataFlowCallableValue() { this = TCallableValue(callable) }
-
- override string toString() { result = callable.toString() }
-
- override CallNode getACall() { result = callable.getACall() }
-
- override Scope getScope() { result = callable.getScope() }
-
- override NameNode getParameter(int n) { result = callable.getParameter(n) }
-
- override string getName() { result = callable.getName() }
-}
-
-class DataFlowClassValue extends DataFlowCallable, TClassValue {
- ClassValue c;
-
- DataFlowClassValue() { this = TClassValue(c) }
-
- override string toString() { result = c.toString() }
-
- override CallNode getACall() { result = c.getACall() }
-
- override Scope getScope() { result = c.getScope() }
-
- override NameNode getParameter(int n) {
- result.getNode() = c.getScope().getInitMethod().getArg(n + 1).asName()
- }
-
- override string getName() { result = c.getName() }
-}
+class DataFlowCallable = CallableValue;
newtype TDataFlowCall =
- TCallNode(CallNode call) or
+ TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or
+ TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or
TSpecialCall(SpecialMethodCallNode special)
abstract class DataFlowCall extends TDataFlowCall {
@@ -292,6 +229,36 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
}
+/** Represents a call to a class. */
+class ClassCall extends DataFlowCall, TClassCall {
+ CallNode call;
+ ClassValue c;
+
+ ClassCall() {
+ this = TClassCall(call) and
+ call = c.getACall()
+ }
+
+ override string toString() { result = call.toString() }
+
+ override ControlFlowNode getArg(int n) {
+ result = call.getArg(n - 1)
+ or
+ n = 0 and result = call
+ }
+
+ override ControlFlowNode getNode() { result = call }
+
+ override DataFlowCallable getCallable() {
+ exists(CallableValue callable |
+ result = callable and
+ c.getScope().getInitMethod() = callable.getScope()
+ )
+ }
+
+ override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
+}
+
/** Represents a call to a special method. */
class SpecialCall extends DataFlowCall, TSpecialCall {
SpecialMethodCallNode special;
@@ -304,9 +271,7 @@ class SpecialCall extends DataFlowCall, TSpecialCall {
override ControlFlowNode getNode() { result = special }
- override DataFlowCallable getCallable() {
- result = TCallableValue(special.getResolvedSpecialMethod())
- }
+ override DataFlowCallable getCallable() { result = special.getResolvedSpecialMethod() }
override DataFlowCallable getEnclosingCallable() {
result.getScope() = special.getNode().getScope()
@@ -333,16 +298,6 @@ class ExplicitArgumentNode extends ArgumentNode {
final override DataFlowCall getCall() { this.argumentOf(result, _) }
}
-class ImplicitSelfArgumentNode extends ArgumentNode {
- ImplicitSelfArgumentNode() { exists(ClassValue cv | node = cv.getACall()) }
-
- /** Holds if this argument occurs at the given position in the given call. */
- override predicate argumentOf(DataFlowCall call, int pos) { call = TCallNode(node) and pos = -1 }
-
- /** Gets the call in which this node is an argument. */
- final override DataFlowCall getCall() { result = TCallNode(node) }
-}
-
/** Gets a viable run-time target for the call `call`. */
DataFlowCallable viableCallable(DataFlowCall call) { result = call.getCallable() }
diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql
index f0db6f193f5..9b7b562c770 100644
--- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql
+++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql
@@ -11,7 +11,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration {
exists(AssignmentDefinition def, DataFlow::DataFlowCall call |
def.getVariable() = node.(DataFlow::EssaNode).getVar() and
def.getValue() = call.getNode() and
- call.getCallable().getName().matches("With\\_%")
+ call.getNode().(CallNode).getNode().(Call).toString().matches("With\\_%") // TODO: Do not rely on toString
) and
node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%")
}
diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
index 650f271bd27..a34e34424a7 100644
--- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
+++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
@@ -1,200 +1,200 @@
-| classes.py:41:16:41:22 | malloc super() | classes.py:41:16:41:22 | malloc super() |
+| classes.py:41:16:41:22 | ControlFlowNode for super() | classes.py:41:16:41:22 | ControlFlowNode for super() |
| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() |
-| classes.py:45:16:45:25 | malloc With_new() | classes.py:45:16:45:25 | malloc With_new() |
-| classes.py:58:17:58:27 | malloc With_init() | classes.py:52:18:52:21 | SSA variable self |
-| classes.py:58:17:58:27 | malloc With_init() | classes.py:58:17:58:27 | malloc With_init() |
-| classes.py:71:16:71:25 | malloc With_del() | classes.py:71:16:71:25 | malloc With_del() |
-| classes.py:86:17:86:27 | malloc With_repr() | classes.py:86:17:86:27 | malloc With_repr() |
-| classes.py:101:16:101:25 | malloc With_str() | classes.py:101:16:101:25 | malloc With_str() |
-| classes.py:102:5:102:17 | malloc str() | classes.py:102:5:102:17 | malloc str() |
-| classes.py:116:18:116:29 | malloc With_bytes() | classes.py:116:18:116:29 | malloc With_bytes() |
-| classes.py:117:5:117:21 | malloc bytes() | classes.py:117:5:117:21 | malloc bytes() |
-| classes.py:132:19:132:31 | malloc With_format() | classes.py:132:19:132:31 | malloc With_format() |
-| classes.py:138:19:138:31 | malloc With_format() | classes.py:138:19:138:31 | malloc With_format() |
-| classes.py:143:19:143:31 | malloc With_format() | classes.py:143:19:143:31 | malloc With_format() |
-| classes.py:159:15:159:23 | malloc With_lt() | classes.py:159:15:159:23 | malloc With_lt() |
-| classes.py:176:15:176:23 | malloc With_le() | classes.py:176:15:176:23 | malloc With_le() |
-| classes.py:193:15:193:23 | malloc With_eq() | classes.py:193:15:193:23 | malloc With_eq() |
-| classes.py:209:15:209:23 | malloc With_ne() | classes.py:209:15:209:23 | malloc With_ne() |
-| classes.py:225:15:225:23 | malloc With_gt() | classes.py:225:15:225:23 | malloc With_gt() |
-| classes.py:242:15:242:23 | malloc With_ge() | classes.py:242:15:242:23 | malloc With_ge() |
-| classes.py:258:17:258:27 | malloc With_hash() | classes.py:258:17:258:27 | malloc With_hash() |
-| classes.py:263:17:263:27 | malloc With_hash() | classes.py:263:17:263:27 | malloc With_hash() |
-| classes.py:264:9:264:24 | malloc set() | classes.py:264:9:264:24 | malloc set() |
-| classes.py:268:17:268:27 | malloc With_hash() | classes.py:268:17:268:27 | malloc With_hash() |
-| classes.py:269:9:269:30 | malloc frozenset() | classes.py:269:9:269:30 | malloc frozenset() |
-| classes.py:273:17:273:27 | malloc With_hash() | classes.py:273:17:273:27 | malloc With_hash() |
-| classes.py:274:9:274:28 | malloc dict() | classes.py:274:9:274:28 | malloc dict() |
-| classes.py:288:17:288:27 | malloc With_bool() | classes.py:288:17:288:27 | malloc With_bool() |
-| classes.py:289:5:289:19 | malloc bool() | classes.py:289:5:289:19 | malloc bool() |
-| classes.py:293:17:293:27 | malloc With_bool() | classes.py:293:17:293:27 | malloc With_bool() |
-| classes.py:311:20:311:33 | malloc With_getattr() | classes.py:311:20:311:33 | malloc With_getattr() |
-| classes.py:327:25:327:43 | malloc With_getattribute() | classes.py:327:25:327:43 | malloc With_getattribute() |
-| classes.py:343:20:343:33 | malloc With_setattr() | classes.py:343:20:343:33 | malloc With_setattr() |
-| classes.py:359:20:359:33 | malloc With_delattr() | classes.py:359:20:359:33 | malloc With_delattr() |
-| classes.py:374:16:374:25 | malloc With_dir() | classes.py:374:16:374:25 | malloc With_dir() |
-| classes.py:399:16:399:25 | malloc With_get() | classes.py:399:16:399:25 | malloc With_get() |
-| classes.py:401:12:401:17 | malloc arg3() | classes.py:401:12:401:17 | malloc arg3() |
-| classes.py:417:16:417:25 | malloc With_set() | classes.py:417:16:417:25 | malloc With_set() |
-| classes.py:419:12:419:18 | malloc Owner() | classes.py:419:12:419:18 | malloc Owner() |
-| classes.py:435:19:435:31 | malloc With_delete() | classes.py:435:19:435:31 | malloc With_delete() |
-| classes.py:437:12:437:18 | malloc Owner() | classes.py:437:12:437:18 | malloc Owner() |
-| classes.py:453:21:453:35 | malloc With_set_name() | classes.py:453:21:453:35 | malloc With_set_name() |
-| classes.py:454:5:454:53 | malloc type() | classes.py:454:5:454:53 | malloc type() |
-| classes.py:454:29:454:52 | malloc dict() | classes.py:454:29:454:52 | malloc dict() |
-| classes.py:473:5:473:47 | malloc type() | classes.py:473:5:473:47 | malloc type() |
-| classes.py:513:26:513:45 | malloc With_instancecheck() | classes.py:513:26:513:45 | malloc With_instancecheck() |
-| classes.py:530:26:530:45 | malloc With_subclasscheck() | classes.py:530:26:530:45 | malloc With_subclasscheck() |
-| classes.py:548:26:548:51 | malloc Subscript() | classes.py:548:26:548:51 | malloc Subscript() |
-| classes.py:561:17:561:27 | malloc With_call() | classes.py:561:17:561:27 | malloc With_call() |
-| classes.py:577:16:577:25 | malloc With_len() | classes.py:577:16:577:25 | malloc With_len() |
-| classes.py:582:16:582:25 | malloc With_len() | classes.py:582:16:582:25 | malloc With_len() |
-| classes.py:583:5:583:18 | malloc bool() | classes.py:583:5:583:18 | malloc bool() |
-| classes.py:587:16:587:25 | malloc With_len() | classes.py:587:16:587:25 | malloc With_len() |
-| classes.py:604:24:604:41 | malloc With_length_hint() | classes.py:604:24:604:41 | malloc With_length_hint() |
-| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:612:21:612:24 | SSA variable self |
-| classes.py:620:20:620:33 | malloc With_getitem() | classes.py:620:20:620:33 | malloc With_getitem() |
+| classes.py:45:16:45:25 | ControlFlowNode for With_new() | classes.py:45:16:45:25 | ControlFlowNode for With_new() |
+| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self |
+| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:58:17:58:27 | ControlFlowNode for With_init() |
+| classes.py:71:16:71:25 | ControlFlowNode for With_del() | classes.py:71:16:71:25 | ControlFlowNode for With_del() |
+| classes.py:86:17:86:27 | ControlFlowNode for With_repr() | classes.py:86:17:86:27 | ControlFlowNode for With_repr() |
+| classes.py:101:16:101:25 | ControlFlowNode for With_str() | classes.py:101:16:101:25 | ControlFlowNode for With_str() |
+| classes.py:102:5:102:17 | ControlFlowNode for str() | classes.py:102:5:102:17 | ControlFlowNode for str() |
+| classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | classes.py:116:18:116:29 | ControlFlowNode for With_bytes() |
+| classes.py:117:5:117:21 | ControlFlowNode for bytes() | classes.py:117:5:117:21 | ControlFlowNode for bytes() |
+| classes.py:132:19:132:31 | ControlFlowNode for With_format() | classes.py:132:19:132:31 | ControlFlowNode for With_format() |
+| classes.py:138:19:138:31 | ControlFlowNode for With_format() | classes.py:138:19:138:31 | ControlFlowNode for With_format() |
+| classes.py:143:19:143:31 | ControlFlowNode for With_format() | classes.py:143:19:143:31 | ControlFlowNode for With_format() |
+| classes.py:159:15:159:23 | ControlFlowNode for With_lt() | classes.py:159:15:159:23 | ControlFlowNode for With_lt() |
+| classes.py:176:15:176:23 | ControlFlowNode for With_le() | classes.py:176:15:176:23 | ControlFlowNode for With_le() |
+| classes.py:193:15:193:23 | ControlFlowNode for With_eq() | classes.py:193:15:193:23 | ControlFlowNode for With_eq() |
+| classes.py:209:15:209:23 | ControlFlowNode for With_ne() | classes.py:209:15:209:23 | ControlFlowNode for With_ne() |
+| classes.py:225:15:225:23 | ControlFlowNode for With_gt() | classes.py:225:15:225:23 | ControlFlowNode for With_gt() |
+| classes.py:242:15:242:23 | ControlFlowNode for With_ge() | classes.py:242:15:242:23 | ControlFlowNode for With_ge() |
+| classes.py:258:17:258:27 | ControlFlowNode for With_hash() | classes.py:258:17:258:27 | ControlFlowNode for With_hash() |
+| classes.py:263:17:263:27 | ControlFlowNode for With_hash() | classes.py:263:17:263:27 | ControlFlowNode for With_hash() |
+| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() |
+| classes.py:268:17:268:27 | ControlFlowNode for With_hash() | classes.py:268:17:268:27 | ControlFlowNode for With_hash() |
+| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() |
+| classes.py:273:17:273:27 | ControlFlowNode for With_hash() | classes.py:273:17:273:27 | ControlFlowNode for With_hash() |
+| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() |
+| classes.py:288:17:288:27 | ControlFlowNode for With_bool() | classes.py:288:17:288:27 | ControlFlowNode for With_bool() |
+| classes.py:289:5:289:19 | ControlFlowNode for bool() | classes.py:289:5:289:19 | ControlFlowNode for bool() |
+| classes.py:293:17:293:27 | ControlFlowNode for With_bool() | classes.py:293:17:293:27 | ControlFlowNode for With_bool() |
+| classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | classes.py:311:20:311:33 | ControlFlowNode for With_getattr() |
+| classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() |
+| classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | classes.py:343:20:343:33 | ControlFlowNode for With_setattr() |
+| classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | classes.py:359:20:359:33 | ControlFlowNode for With_delattr() |
+| classes.py:374:16:374:25 | ControlFlowNode for With_dir() | classes.py:374:16:374:25 | ControlFlowNode for With_dir() |
+| classes.py:399:16:399:25 | ControlFlowNode for With_get() | classes.py:399:16:399:25 | ControlFlowNode for With_get() |
+| classes.py:401:12:401:17 | ControlFlowNode for arg3() | classes.py:401:12:401:17 | ControlFlowNode for arg3() |
+| classes.py:417:16:417:25 | ControlFlowNode for With_set() | classes.py:417:16:417:25 | ControlFlowNode for With_set() |
+| classes.py:419:12:419:18 | ControlFlowNode for Owner() | classes.py:419:12:419:18 | ControlFlowNode for Owner() |
+| classes.py:435:19:435:31 | ControlFlowNode for With_delete() | classes.py:435:19:435:31 | ControlFlowNode for With_delete() |
+| classes.py:437:12:437:18 | ControlFlowNode for Owner() | classes.py:437:12:437:18 | ControlFlowNode for Owner() |
+| classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | classes.py:453:21:453:35 | ControlFlowNode for With_set_name() |
+| classes.py:454:5:454:53 | ControlFlowNode for type() | classes.py:454:5:454:53 | ControlFlowNode for type() |
+| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() |
+| classes.py:473:5:473:47 | ControlFlowNode for type() | classes.py:473:5:473:47 | ControlFlowNode for type() |
+| classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() |
+| classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() |
+| classes.py:548:26:548:51 | ControlFlowNode for Subscript() | classes.py:548:26:548:51 | ControlFlowNode for Subscript() |
+| classes.py:561:17:561:27 | ControlFlowNode for With_call() | classes.py:561:17:561:27 | ControlFlowNode for With_call() |
+| classes.py:577:16:577:25 | ControlFlowNode for With_len() | classes.py:577:16:577:25 | ControlFlowNode for With_len() |
+| classes.py:582:16:582:25 | ControlFlowNode for With_len() | classes.py:582:16:582:25 | ControlFlowNode for With_len() |
+| classes.py:583:5:583:18 | ControlFlowNode for bool() | classes.py:583:5:583:18 | ControlFlowNode for bool() |
+| classes.py:587:16:587:25 | ControlFlowNode for With_len() | classes.py:587:16:587:25 | ControlFlowNode for With_len() |
+| classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() |
+| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:612:21:612:24 | SSA variable self |
+| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:620:20:620:33 | ControlFlowNode for With_getitem() |
| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self |
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key |
-| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:629:21:629:24 | SSA variable self |
-| classes.py:637:20:637:33 | malloc With_setitem() | classes.py:637:20:637:33 | malloc With_setitem() |
+| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:629:21:629:24 | SSA variable self |
+| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:637:20:637:33 | ControlFlowNode for With_setitem() |
| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self |
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key |
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value |
-| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:647:21:647:24 | SSA variable self |
-| classes.py:654:20:654:33 | malloc With_delitem() | classes.py:654:20:654:33 | malloc With_delitem() |
+| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:647:21:647:24 | SSA variable self |
+| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:654:20:654:33 | ControlFlowNode for With_delitem() |
| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self |
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key |
-| classes.py:671:20:671:33 | malloc With_missing() | classes.py:671:20:671:33 | malloc With_missing() |
+| classes.py:671:20:671:33 | ControlFlowNode for With_missing() | classes.py:671:20:671:33 | ControlFlowNode for With_missing() |
| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() |
-| classes.py:687:17:687:27 | malloc With_iter() | classes.py:687:17:687:27 | malloc With_iter() |
-| classes.py:702:21:702:35 | malloc With_reversed() | classes.py:702:21:702:35 | malloc With_reversed() |
-| classes.py:703:5:703:27 | malloc reversed() | classes.py:703:5:703:27 | malloc reversed() |
-| classes.py:718:21:718:35 | malloc With_contains() | classes.py:718:21:718:35 | malloc With_contains() |
-| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:17:727:20 | SSA variable self |
-| classes.py:735:16:735:25 | malloc With_add() | classes.py:727:23:727:27 | SSA variable other |
-| classes.py:735:16:735:25 | malloc With_add() | classes.py:735:16:735:25 | malloc With_add() |
+| classes.py:687:17:687:27 | ControlFlowNode for With_iter() | classes.py:687:17:687:27 | ControlFlowNode for With_iter() |
+| classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | classes.py:702:21:702:35 | ControlFlowNode for With_reversed() |
+| classes.py:703:5:703:27 | ControlFlowNode for reversed() | classes.py:703:5:703:27 | ControlFlowNode for reversed() |
+| classes.py:718:21:718:35 | ControlFlowNode for With_contains() | classes.py:718:21:718:35 | ControlFlowNode for With_contains() |
+| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:17:727:20 | SSA variable self |
+| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:23:727:27 | SSA variable other |
+| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:735:16:735:25 | ControlFlowNode for With_add() |
| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self |
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other |
-| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:17:744:20 | SSA variable self |
-| classes.py:752:16:752:25 | malloc With_sub() | classes.py:744:23:744:27 | SSA variable other |
-| classes.py:752:16:752:25 | malloc With_sub() | classes.py:752:16:752:25 | malloc With_sub() |
+| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:17:744:20 | SSA variable self |
+| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:23:744:27 | SSA variable other |
+| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:752:16:752:25 | ControlFlowNode for With_sub() |
| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self |
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other |
-| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:17:761:20 | SSA variable self |
-| classes.py:769:16:769:25 | malloc With_mul() | classes.py:761:23:761:27 | SSA variable other |
-| classes.py:769:16:769:25 | malloc With_mul() | classes.py:769:16:769:25 | malloc With_mul() |
+| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:17:761:20 | SSA variable self |
+| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:23:761:27 | SSA variable other |
+| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:769:16:769:25 | ControlFlowNode for With_mul() |
| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self |
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other |
-| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:20:778:23 | SSA variable self |
-| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:778:26:778:30 | SSA variable other |
-| classes.py:786:19:786:31 | malloc With_matmul() | classes.py:786:19:786:31 | malloc With_matmul() |
+| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:20:778:23 | SSA variable self |
+| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:26:778:30 | SSA variable other |
+| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:786:19:786:31 | ControlFlowNode for With_matmul() |
| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self |
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other |
-| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:21:795:24 | SSA variable self |
-| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:795:27:795:31 | SSA variable other |
-| classes.py:803:20:803:33 | malloc With_truediv() | classes.py:803:20:803:33 | malloc With_truediv() |
+| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:21:795:24 | SSA variable self |
+| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:27:795:31 | SSA variable other |
+| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:803:20:803:33 | ControlFlowNode for With_truediv() |
| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self |
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other |
-| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:22:812:25 | SSA variable self |
-| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:812:28:812:32 | SSA variable other |
-| classes.py:820:21:820:35 | malloc With_floordiv() | classes.py:820:21:820:35 | malloc With_floordiv() |
+| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:22:812:25 | SSA variable self |
+| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:28:812:32 | SSA variable other |
+| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() |
| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self |
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other |
-| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:17:829:20 | SSA variable self |
-| classes.py:837:16:837:25 | malloc With_mod() | classes.py:829:23:829:27 | SSA variable other |
-| classes.py:837:16:837:25 | malloc With_mod() | classes.py:837:16:837:25 | malloc With_mod() |
+| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:17:829:20 | SSA variable self |
+| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:23:829:27 | SSA variable other |
+| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:837:16:837:25 | ControlFlowNode for With_mod() |
| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self |
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other |
-| classes.py:854:19:854:31 | malloc With_divmod() | classes.py:854:19:854:31 | malloc With_divmod() |
-| classes.py:871:16:871:25 | malloc With_pow() | classes.py:871:16:871:25 | malloc With_pow() |
-| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:17:863:20 | SSA variable self |
-| classes.py:877:16:877:25 | malloc With_pow() | classes.py:863:23:863:27 | SSA variable other |
-| classes.py:877:16:877:25 | malloc With_pow() | classes.py:877:16:877:25 | malloc With_pow() |
+| classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | classes.py:854:19:854:31 | ControlFlowNode for With_divmod() |
+| classes.py:871:16:871:25 | ControlFlowNode for With_pow() | classes.py:871:16:871:25 | ControlFlowNode for With_pow() |
+| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:17:863:20 | SSA variable self |
+| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:23:863:27 | SSA variable other |
+| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:877:16:877:25 | ControlFlowNode for With_pow() |
| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self |
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other |
-| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:20:886:23 | SSA variable self |
-| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:886:26:886:30 | SSA variable other |
-| classes.py:894:19:894:31 | malloc With_lshift() | classes.py:894:19:894:31 | malloc With_lshift() |
+| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:20:886:23 | SSA variable self |
+| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:26:886:30 | SSA variable other |
+| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:894:19:894:31 | ControlFlowNode for With_lshift() |
| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self |
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other |
-| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:20:903:23 | SSA variable self |
-| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:903:26:903:30 | SSA variable other |
-| classes.py:911:19:911:31 | malloc With_rshift() | classes.py:911:19:911:31 | malloc With_rshift() |
+| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:20:903:23 | SSA variable self |
+| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:26:903:30 | SSA variable other |
+| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:911:19:911:31 | ControlFlowNode for With_rshift() |
| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self |
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other |
-| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:17:920:20 | SSA variable self |
-| classes.py:928:16:928:25 | malloc With_and() | classes.py:920:23:920:27 | SSA variable other |
-| classes.py:928:16:928:25 | malloc With_and() | classes.py:928:16:928:25 | malloc With_and() |
+| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:17:920:20 | SSA variable self |
+| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:23:920:27 | SSA variable other |
+| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:928:16:928:25 | ControlFlowNode for With_and() |
| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self |
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other |
-| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:17:937:20 | SSA variable self |
-| classes.py:945:16:945:25 | malloc With_xor() | classes.py:937:23:937:27 | SSA variable other |
-| classes.py:945:16:945:25 | malloc With_xor() | classes.py:945:16:945:25 | malloc With_xor() |
+| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:17:937:20 | SSA variable self |
+| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:23:937:27 | SSA variable other |
+| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:945:16:945:25 | ControlFlowNode for With_xor() |
| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self |
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other |
-| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:16:954:19 | SSA variable self |
-| classes.py:962:15:962:23 | malloc With_or() | classes.py:954:22:954:26 | SSA variable other |
-| classes.py:962:15:962:23 | malloc With_or() | classes.py:962:15:962:23 | malloc With_or() |
+| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:16:954:19 | SSA variable self |
+| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:22:954:26 | SSA variable other |
+| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:962:15:962:23 | ControlFlowNode for With_or() |
| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self |
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other |
-| classes.py:979:17:979:27 | malloc With_radd() | classes.py:979:17:979:27 | malloc With_radd() |
-| classes.py:996:17:996:27 | malloc With_rsub() | classes.py:996:17:996:27 | malloc With_rsub() |
-| classes.py:1013:17:1013:27 | malloc With_rmul() | classes.py:1013:17:1013:27 | malloc With_rmul() |
-| classes.py:1030:20:1030:33 | malloc With_rmatmul() | classes.py:1030:20:1030:33 | malloc With_rmatmul() |
-| classes.py:1047:21:1047:35 | malloc With_rtruediv() | classes.py:1047:21:1047:35 | malloc With_rtruediv() |
-| classes.py:1064:22:1064:37 | malloc With_rfloordiv() | classes.py:1064:22:1064:37 | malloc With_rfloordiv() |
-| classes.py:1081:17:1081:27 | malloc With_rmod() | classes.py:1081:17:1081:27 | malloc With_rmod() |
-| classes.py:1098:20:1098:33 | malloc With_rdivmod() | classes.py:1098:20:1098:33 | malloc With_rdivmod() |
-| classes.py:1115:17:1115:27 | malloc With_rpow() | classes.py:1115:17:1115:27 | malloc With_rpow() |
-| classes.py:1121:17:1121:27 | malloc With_rpow() | classes.py:1121:17:1121:27 | malloc With_rpow() |
-| classes.py:1138:20:1138:33 | malloc With_rlshift() | classes.py:1138:20:1138:33 | malloc With_rlshift() |
-| classes.py:1155:20:1155:33 | malloc With_rrshift() | classes.py:1155:20:1155:33 | malloc With_rrshift() |
-| classes.py:1172:17:1172:27 | malloc With_rand() | classes.py:1172:17:1172:27 | malloc With_rand() |
-| classes.py:1189:17:1189:27 | malloc With_rxor() | classes.py:1189:17:1189:27 | malloc With_rxor() |
-| classes.py:1206:16:1206:25 | malloc With_ror() | classes.py:1206:16:1206:25 | malloc With_ror() |
-| classes.py:1223:17:1223:27 | malloc With_iadd() | classes.py:1223:17:1223:27 | malloc With_iadd() |
-| classes.py:1240:17:1240:27 | malloc With_isub() | classes.py:1240:17:1240:27 | malloc With_isub() |
-| classes.py:1257:17:1257:27 | malloc With_imul() | classes.py:1257:17:1257:27 | malloc With_imul() |
-| classes.py:1274:20:1274:33 | malloc With_imatmul() | classes.py:1274:20:1274:33 | malloc With_imatmul() |
-| classes.py:1291:21:1291:35 | malloc With_itruediv() | classes.py:1291:21:1291:35 | malloc With_itruediv() |
-| classes.py:1308:22:1308:37 | malloc With_ifloordiv() | classes.py:1308:22:1308:37 | malloc With_ifloordiv() |
-| classes.py:1325:17:1325:27 | malloc With_imod() | classes.py:1325:17:1325:27 | malloc With_imod() |
-| classes.py:1342:17:1342:27 | malloc With_ipow() | classes.py:1342:17:1342:27 | malloc With_ipow() |
-| classes.py:1359:20:1359:33 | malloc With_ilshift() | classes.py:1359:20:1359:33 | malloc With_ilshift() |
-| classes.py:1376:20:1376:33 | malloc With_irshift() | classes.py:1376:20:1376:33 | malloc With_irshift() |
-| classes.py:1393:17:1393:27 | malloc With_iand() | classes.py:1393:17:1393:27 | malloc With_iand() |
-| classes.py:1410:17:1410:27 | malloc With_ixor() | classes.py:1410:17:1410:27 | malloc With_ixor() |
-| classes.py:1427:16:1427:25 | malloc With_ior() | classes.py:1427:16:1427:25 | malloc With_ior() |
-| classes.py:1443:16:1443:25 | malloc With_neg() | classes.py:1443:16:1443:25 | malloc With_neg() |
-| classes.py:1458:16:1458:25 | malloc With_pos() | classes.py:1458:16:1458:25 | malloc With_pos() |
-| classes.py:1473:16:1473:25 | malloc With_abs() | classes.py:1473:16:1473:25 | malloc With_abs() |
-| classes.py:1488:19:1488:31 | malloc With_invert() | classes.py:1488:19:1488:31 | malloc With_invert() |
-| classes.py:1503:20:1503:33 | malloc With_complex() | classes.py:1503:20:1503:33 | malloc With_complex() |
-| classes.py:1504:5:1504:25 | malloc complex() | classes.py:1504:5:1504:25 | malloc complex() |
-| classes.py:1518:16:1518:25 | malloc With_int() | classes.py:1518:16:1518:25 | malloc With_int() |
-| classes.py:1519:5:1519:17 | malloc int() | classes.py:1519:5:1519:17 | malloc int() |
-| classes.py:1533:18:1533:29 | malloc With_float() | classes.py:1533:18:1533:29 | malloc With_float() |
-| classes.py:1534:5:1534:21 | malloc float() | classes.py:1534:5:1534:21 | malloc float() |
-| classes.py:1549:18:1549:29 | malloc With_index() | classes.py:1549:18:1549:29 | malloc With_index() |
-| classes.py:1554:18:1554:29 | malloc With_index() | classes.py:1554:18:1554:29 | malloc With_index() |
-| classes.py:1559:18:1559:29 | malloc With_index() | classes.py:1559:18:1559:29 | malloc With_index() |
-| classes.py:1564:18:1564:29 | malloc With_index() | classes.py:1564:18:1564:29 | malloc With_index() |
-| classes.py:1569:18:1569:29 | malloc With_index() | classes.py:1569:18:1569:29 | malloc With_index() |
-| classes.py:1574:18:1574:29 | malloc With_index() | classes.py:1574:18:1574:29 | malloc With_index() |
-| classes.py:1575:5:1575:19 | malloc int() | classes.py:1575:5:1575:19 | malloc int() |
-| classes.py:1579:18:1579:29 | malloc With_index() | classes.py:1579:18:1579:29 | malloc With_index() |
-| classes.py:1580:5:1580:21 | malloc float() | classes.py:1580:5:1580:21 | malloc float() |
-| classes.py:1584:18:1584:29 | malloc With_index() | classes.py:1584:18:1584:29 | malloc With_index() |
-| classes.py:1585:5:1585:23 | malloc complex() | classes.py:1585:5:1585:23 | malloc complex() |
-| classes.py:1599:18:1599:29 | malloc With_round() | classes.py:1599:18:1599:29 | malloc With_round() |
-| classes.py:1614:18:1614:29 | malloc With_trunc() | classes.py:1614:18:1614:29 | malloc With_trunc() |
-| classes.py:1630:18:1630:29 | malloc With_floor() | classes.py:1630:18:1630:29 | malloc With_floor() |
-| classes.py:1646:17:1646:27 | malloc With_ceil() | classes.py:1646:17:1646:27 | malloc With_ceil() |
-| classes.py:1665:10:1665:21 | malloc With_enter() | classes.py:1665:10:1665:21 | malloc With_enter() |
-| classes.py:1686:10:1686:20 | malloc With_exit() | classes.py:1686:10:1686:20 | malloc With_exit() |
-| classes.py:1703:18:1703:29 | malloc With_await() | classes.py:1703:18:1703:29 | malloc With_await() |
-| classes.py:1726:18:1726:29 | malloc With_aiter() | classes.py:1726:18:1726:29 | malloc With_aiter() |
-| classes.py:1745:18:1745:29 | malloc With_anext() | classes.py:1745:18:1745:29 | malloc With_anext() |
-| classes.py:1763:19:1763:31 | malloc With_aenter() | classes.py:1763:19:1763:31 | malloc With_aenter() |
-| classes.py:1784:18:1784:29 | malloc With_aexit() | classes.py:1784:18:1784:29 | malloc With_aexit() |
+| classes.py:979:17:979:27 | ControlFlowNode for With_radd() | classes.py:979:17:979:27 | ControlFlowNode for With_radd() |
+| classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | classes.py:996:17:996:27 | ControlFlowNode for With_rsub() |
+| classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() |
+| classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() |
+| classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() |
+| classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() |
+| classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() |
+| classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() |
+| classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() |
+| classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() |
+| classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() |
+| classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() |
+| classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() |
+| classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() |
+| classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() |
+| classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() |
+| classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() |
+| classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() |
+| classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() |
+| classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() |
+| classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() |
+| classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() |
+| classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() |
+| classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() |
+| classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() |
+| classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() |
+| classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() |
+| classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() |
+| classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() |
+| classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() |
+| classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() |
+| classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() |
+| classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() |
+| classes.py:1504:5:1504:25 | ControlFlowNode for complex() | classes.py:1504:5:1504:25 | ControlFlowNode for complex() |
+| classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | classes.py:1518:16:1518:25 | ControlFlowNode for With_int() |
+| classes.py:1519:5:1519:17 | ControlFlowNode for int() | classes.py:1519:5:1519:17 | ControlFlowNode for int() |
+| classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | classes.py:1533:18:1533:29 | ControlFlowNode for With_float() |
+| classes.py:1534:5:1534:21 | ControlFlowNode for float() | classes.py:1534:5:1534:21 | ControlFlowNode for float() |
+| classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | classes.py:1549:18:1549:29 | ControlFlowNode for With_index() |
+| classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | classes.py:1554:18:1554:29 | ControlFlowNode for With_index() |
+| classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | classes.py:1559:18:1559:29 | ControlFlowNode for With_index() |
+| classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | classes.py:1564:18:1564:29 | ControlFlowNode for With_index() |
+| classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | classes.py:1569:18:1569:29 | ControlFlowNode for With_index() |
+| classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | classes.py:1574:18:1574:29 | ControlFlowNode for With_index() |
+| classes.py:1575:5:1575:19 | ControlFlowNode for int() | classes.py:1575:5:1575:19 | ControlFlowNode for int() |
+| classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | classes.py:1579:18:1579:29 | ControlFlowNode for With_index() |
+| classes.py:1580:5:1580:21 | ControlFlowNode for float() | classes.py:1580:5:1580:21 | ControlFlowNode for float() |
+| classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | classes.py:1584:18:1584:29 | ControlFlowNode for With_index() |
+| classes.py:1585:5:1585:23 | ControlFlowNode for complex() | classes.py:1585:5:1585:23 | ControlFlowNode for complex() |
+| classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | classes.py:1599:18:1599:29 | ControlFlowNode for With_round() |
+| classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() |
+| classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() |
+| classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() |
+| classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() |
+| classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() |
+| classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | classes.py:1703:18:1703:29 | ControlFlowNode for With_await() |
+| classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() |
+| classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() |
+| classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() |
+| classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
index 1e36d80a5ee..7cc18f44d0b 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
@@ -17,28 +17,28 @@
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x |
| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
index 47ab357dc2c..d6b2ba62757 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
@@ -1,4 +1,5 @@
edges
+| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute |
@@ -8,7 +9,13 @@ edges
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
nodes
+| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
+| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] |
@@ -20,6 +27,15 @@ nodes
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | semmle.label | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
+| test.py:46:10:46:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
#select
+| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | |
| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | |
| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | |
+| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | |
+| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
index d68142f928f..f65f714063c 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
@@ -21,23 +21,20 @@ edges
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
-| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post malloc] malloc MyObj() [Attribute foo] |
-| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
-| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post malloc] malloc MyObj() [Attribute foo] |
-| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:17:50:17 | [post arg] ControlFlowNode for x [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
-| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE [Attribute foo] |
#select
| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
index 0fe90c99f8f..b0fedf775a3 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
@@ -1,7 +1,5 @@
| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj |
| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj |
-| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
-| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self |
@@ -14,10 +12,21 @@
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] |
+| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] |
| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj |
| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj |
-| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ |
-| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ |
| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj |
| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self |
@@ -28,12 +37,14 @@
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
-| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:15:20:15:30 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] |
+| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
-| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
-| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj |
| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo |
@@ -52,6 +63,7 @@
| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj |
+| test.py:21:12:21:14 | SSA variable obj [Attribute foo] | test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] |
| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x |
@@ -60,6 +72,8 @@
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
+| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute |
+| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute |
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj |
@@ -85,30 +99,38 @@
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | malloc MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
+| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:21:12:21:14 | SSA variable obj [Attribute foo] |
+| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
@@ -143,32 +165,43 @@
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:14:18:14:21 | SSA variable self |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | malloc NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
@@ -203,18 +236,24 @@
| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | malloc MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
+| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj |
@@ -229,26 +268,44 @@
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
+| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
+| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
+| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
+| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
@@ -263,3 +320,5 @@
| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
+| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
index 6651727c229..03549a5b810 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
@@ -1,14 +1,14 @@
| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x |
| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x |
| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
-| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj |
-| examples.py:46:9:46:16 | malloc MyObj() | examples.py:46:3:46:5 | SSA variable obj |
+| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
+| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a |
| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | malloc MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
index 4bbb7f3542b..434cd0b6bda 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
@@ -1,52 +1,52 @@
| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self |
| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self |
-| examples.py:14:20:14:30 | [post malloc] malloc MyObj() | examples.py:14:20:14:30 | malloc MyObj() |
+| examples.py:14:20:14:30 | [post arg] ControlFlowNode for MyObj() | examples.py:14:20:14:30 | ControlFlowNode for MyObj() |
| examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str |
| examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self |
| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj |
| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj |
-| examples.py:25:9:25:19 | [post malloc] malloc MyObj() | examples.py:25:9:25:19 | malloc MyObj() |
+| examples.py:25:9:25:19 | [post arg] ControlFlowNode for MyObj() | examples.py:25:9:25:19 | ControlFlowNode for MyObj() |
| examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str |
| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj |
| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE |
| examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj |
-| examples.py:33:5:33:15 | [post malloc] malloc NestedObj() | examples.py:33:5:33:15 | malloc NestedObj() |
+| examples.py:33:5:33:15 | [post arg] ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() |
| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a |
| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute |
| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a |
| examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() |
| examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a |
| examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute |
-| examples.py:41:7:41:19 | [post malloc] malloc MyObj() | examples.py:41:7:41:19 | malloc MyObj() |
+| examples.py:41:7:41:19 | [post arg] ControlFlowNode for MyObj() | examples.py:41:7:41:19 | ControlFlowNode for MyObj() |
| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
| examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj |
-| examples.py:46:9:46:16 | [post malloc] malloc MyObj() | examples.py:46:9:46:16 | malloc MyObj() |
+| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:9:46:16 | ControlFlowNode for MyObj() |
| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x |
| examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
| examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self |
| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self |
-| test.py:15:20:15:30 | [post malloc] malloc MyObj() | test.py:15:20:15:30 | malloc MyObj() |
+| test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | test.py:15:20:15:30 | ControlFlowNode for MyObj() |
| test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str |
| test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj |
| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:27:13:27:23 | [post malloc] malloc MyObj() | test.py:27:13:27:23 | malloc MyObj() |
+| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:13:27:23 | ControlFlowNode for MyObj() |
| test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
| test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:36:9:36:19 | [post malloc] malloc NestedObj() | test.py:36:9:36:19 | malloc NestedObj() |
+| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:9:36:19 | ControlFlowNode for NestedObj() |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a |
| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute |
| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() |
| test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute |
-| test.py:45:11:45:23 | [post malloc] malloc MyObj() | test.py:45:11:45:23 | malloc MyObj() |
+| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:11:45:23 | ControlFlowNode for MyObj() |
| test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
| test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post malloc] malloc MyObj() | test.py:50:11:50:18 | malloc MyObj() |
+| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:11:50:18 | ControlFlowNode for MyObj() |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
From 87d4e13584297992d169cf7fc6816f94fb5543cf Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 17 Sep 2020 12:42:37 +0200
Subject: [PATCH 038/185] added support for ES2021 assignment operators
---
.../src/com/semmle/jcorn/Parser.java | 20 +-
.../extractor/tests/es2021/input/assign.js | 9 +
.../extractor/tests/es2021/options.json | 3 +
.../tests/es2021/output/trap/assign.js.trap | 633 ++++++++++++++++++
4 files changed, 659 insertions(+), 6 deletions(-)
create mode 100644 javascript/extractor/tests/es2021/input/assign.js
create mode 100644 javascript/extractor/tests/es2021/options.json
create mode 100644 javascript/extractor/tests/es2021/output/trap/assign.js.trap
diff --git a/javascript/extractor/src/com/semmle/jcorn/Parser.java b/javascript/extractor/src/com/semmle/jcorn/Parser.java
index a8348c3c307..7c018b6a456 100644
--- a/javascript/extractor/src/com/semmle/jcorn/Parser.java
+++ b/javascript/extractor/src/com/semmle/jcorn/Parser.java
@@ -531,9 +531,14 @@ public class Parser {
int next2 = charAt(this.pos + 2);
if (this.options.esnext()) {
if (next == '.' && !('0' <= next2 && next2 <= '9')) // '?.', but not '?.X' where X is a digit
- return this.finishOp(TokenType.questiondot, 2);
- if (next == '?') // '??'
- return this.finishOp(TokenType.questionquestion, 2);
+ return this.finishOp(TokenType.questiondot, 2);
+ if (next == '?') { // '??'
+ if (next2 == '=') { // ??=
+ return this.finishOp(TokenType.assign, 3);
+ }
+ return this.finishOp(TokenType.questionquestion, 2);
+ }
+
}
return this.finishOp(TokenType.question, 1);
}
@@ -566,8 +571,11 @@ public class Parser {
private Token readToken_pipe_amp(int code) { // '|&'
int next = charAt(this.pos + 1);
- if (next == code)
+ int next2 = charAt(this.pos + 2);
+ if (next == code) { // && ||
+ if (next2 == 61) return this.finishOp(TokenType.assign, 3); // &&= ||=
return this.finishOp(code == 124 ? TokenType.logicalOR : TokenType.logicalAND, 2);
+ }
if (next == 61) return this.finishOp(TokenType.assign, 2);
return this.finishOp(code == 124 ? TokenType.bitwiseOR : TokenType.bitwiseAND, 1);
}
@@ -709,8 +717,8 @@ public class Parser {
case 42: // '%*'
return this.readToken_mult_modulo_exp(code);
- case 124:
- case 38: // '|&'
+ case 124: // '|'
+ case 38: // '&'
return this.readToken_pipe_amp(code);
case 94: // '^'
diff --git a/javascript/extractor/tests/es2021/input/assign.js b/javascript/extractor/tests/es2021/input/assign.js
new file mode 100644
index 00000000000..7075534fe3b
--- /dev/null
+++ b/javascript/extractor/tests/es2021/input/assign.js
@@ -0,0 +1,9 @@
+var x = 1;
+var y = 2;
+var foo = x && y;
+var bar = x &&= y;
+console.log(x); // 2
+
+x &&= y;
+x ||= y;
+x ??= y;
diff --git a/javascript/extractor/tests/es2021/options.json b/javascript/extractor/tests/es2021/options.json
new file mode 100644
index 00000000000..075583ca1f6
--- /dev/null
+++ b/javascript/extractor/tests/es2021/options.json
@@ -0,0 +1,3 @@
+{
+ "experimental": true
+}
diff --git a/javascript/extractor/tests/es2021/output/trap/assign.js.trap b/javascript/extractor/tests/es2021/output/trap/assign.js.trap
new file mode 100644
index 00000000000..1fb2b771f15
--- /dev/null
+++ b/javascript/extractor/tests/es2021/output/trap/assign.js.trap
@@ -0,0 +1,633 @@
+#10000=@"/assign.js;sourcefile"
+files(#10000,"/assign.js","assign","js",0)
+#10001=@"/;folder"
+folders(#10001,"/","")
+containerparent(#10001,#10000)
+#10002=@"loc,{#10000},0,0,0,0"
+locations_default(#10002,#10000,0,0,0,0)
+hasLocation(#10000,#10002)
+#20000=@"global_scope"
+scopes(#20000,0)
+#20001=@"script;{#10000},1,1"
+#20002=*
+comments(#20002,0,#20001," 2","// 2")
+#20003=@"loc,{#10000},5,17,5,20"
+locations_default(#20003,#10000,5,17,5,20)
+hasLocation(#20002,#20003)
+#20004=*
+lines(#20004,#20001,"var x = 1;","
+")
+#20005=@"loc,{#10000},1,1,1,10"
+locations_default(#20005,#10000,1,1,1,10)
+hasLocation(#20004,#20005)
+#20006=*
+lines(#20006,#20001,"var y = 2;","
+")
+#20007=@"loc,{#10000},2,1,2,10"
+locations_default(#20007,#10000,2,1,2,10)
+hasLocation(#20006,#20007)
+#20008=*
+lines(#20008,#20001,"var foo = x && y;","
+")
+#20009=@"loc,{#10000},3,1,3,17"
+locations_default(#20009,#10000,3,1,3,17)
+hasLocation(#20008,#20009)
+#20010=*
+lines(#20010,#20001,"var bar = x &&= y;","
+")
+#20011=@"loc,{#10000},4,1,4,18"
+locations_default(#20011,#10000,4,1,4,18)
+hasLocation(#20010,#20011)
+#20012=*
+lines(#20012,#20001,"console.log(x); // 2","
+")
+#20013=@"loc,{#10000},5,1,5,20"
+locations_default(#20013,#10000,5,1,5,20)
+hasLocation(#20012,#20013)
+#20014=*
+lines(#20014,#20001,"","
+")
+#20015=@"loc,{#10000},6,1,6,0"
+locations_default(#20015,#10000,6,1,6,0)
+hasLocation(#20014,#20015)
+#20016=*
+lines(#20016,#20001,"x &&= y;","
+")
+#20017=@"loc,{#10000},7,1,7,8"
+locations_default(#20017,#10000,7,1,7,8)
+hasLocation(#20016,#20017)
+#20018=*
+lines(#20018,#20001,"x ||= y;","
+")
+#20019=@"loc,{#10000},8,1,8,8"
+locations_default(#20019,#10000,8,1,8,8)
+hasLocation(#20018,#20019)
+#20020=*
+lines(#20020,#20001,"x ??= y;","
+")
+#20021=@"loc,{#10000},9,1,9,8"
+locations_default(#20021,#10000,9,1,9,8)
+hasLocation(#20020,#20021)
+numlines(#20001,9,8,1)
+#20022=*
+tokeninfo(#20022,7,#20001,0,"var")
+#20023=@"loc,{#10000},1,1,1,3"
+locations_default(#20023,#10000,1,1,1,3)
+hasLocation(#20022,#20023)
+#20024=*
+tokeninfo(#20024,6,#20001,1,"x")
+#20025=@"loc,{#10000},1,5,1,5"
+locations_default(#20025,#10000,1,5,1,5)
+hasLocation(#20024,#20025)
+#20026=*
+tokeninfo(#20026,8,#20001,2,"=")
+#20027=@"loc,{#10000},1,7,1,7"
+locations_default(#20027,#10000,1,7,1,7)
+hasLocation(#20026,#20027)
+#20028=*
+tokeninfo(#20028,3,#20001,3,"1")
+#20029=@"loc,{#10000},1,9,1,9"
+locations_default(#20029,#10000,1,9,1,9)
+hasLocation(#20028,#20029)
+#20030=*
+tokeninfo(#20030,8,#20001,4,";")
+#20031=@"loc,{#10000},1,10,1,10"
+locations_default(#20031,#10000,1,10,1,10)
+hasLocation(#20030,#20031)
+#20032=*
+tokeninfo(#20032,7,#20001,5,"var")
+#20033=@"loc,{#10000},2,1,2,3"
+locations_default(#20033,#10000,2,1,2,3)
+hasLocation(#20032,#20033)
+#20034=*
+tokeninfo(#20034,6,#20001,6,"y")
+#20035=@"loc,{#10000},2,5,2,5"
+locations_default(#20035,#10000,2,5,2,5)
+hasLocation(#20034,#20035)
+#20036=*
+tokeninfo(#20036,8,#20001,7,"=")
+#20037=@"loc,{#10000},2,7,2,7"
+locations_default(#20037,#10000,2,7,2,7)
+hasLocation(#20036,#20037)
+#20038=*
+tokeninfo(#20038,3,#20001,8,"2")
+#20039=@"loc,{#10000},2,9,2,9"
+locations_default(#20039,#10000,2,9,2,9)
+hasLocation(#20038,#20039)
+#20040=*
+tokeninfo(#20040,8,#20001,9,";")
+#20041=@"loc,{#10000},2,10,2,10"
+locations_default(#20041,#10000,2,10,2,10)
+hasLocation(#20040,#20041)
+#20042=*
+tokeninfo(#20042,7,#20001,10,"var")
+#20043=@"loc,{#10000},3,1,3,3"
+locations_default(#20043,#10000,3,1,3,3)
+hasLocation(#20042,#20043)
+#20044=*
+tokeninfo(#20044,6,#20001,11,"foo")
+#20045=@"loc,{#10000},3,5,3,7"
+locations_default(#20045,#10000,3,5,3,7)
+hasLocation(#20044,#20045)
+#20046=*
+tokeninfo(#20046,8,#20001,12,"=")
+#20047=@"loc,{#10000},3,9,3,9"
+locations_default(#20047,#10000,3,9,3,9)
+hasLocation(#20046,#20047)
+#20048=*
+tokeninfo(#20048,6,#20001,13,"x")
+#20049=@"loc,{#10000},3,11,3,11"
+locations_default(#20049,#10000,3,11,3,11)
+hasLocation(#20048,#20049)
+#20050=*
+tokeninfo(#20050,8,#20001,14,"&&")
+#20051=@"loc,{#10000},3,13,3,14"
+locations_default(#20051,#10000,3,13,3,14)
+hasLocation(#20050,#20051)
+#20052=*
+tokeninfo(#20052,6,#20001,15,"y")
+#20053=@"loc,{#10000},3,16,3,16"
+locations_default(#20053,#10000,3,16,3,16)
+hasLocation(#20052,#20053)
+#20054=*
+tokeninfo(#20054,8,#20001,16,";")
+#20055=@"loc,{#10000},3,17,3,17"
+locations_default(#20055,#10000,3,17,3,17)
+hasLocation(#20054,#20055)
+#20056=*
+tokeninfo(#20056,7,#20001,17,"var")
+#20057=@"loc,{#10000},4,1,4,3"
+locations_default(#20057,#10000,4,1,4,3)
+hasLocation(#20056,#20057)
+#20058=*
+tokeninfo(#20058,6,#20001,18,"bar")
+#20059=@"loc,{#10000},4,5,4,7"
+locations_default(#20059,#10000,4,5,4,7)
+hasLocation(#20058,#20059)
+#20060=*
+tokeninfo(#20060,8,#20001,19,"=")
+#20061=@"loc,{#10000},4,9,4,9"
+locations_default(#20061,#10000,4,9,4,9)
+hasLocation(#20060,#20061)
+#20062=*
+tokeninfo(#20062,6,#20001,20,"x")
+#20063=@"loc,{#10000},4,11,4,11"
+locations_default(#20063,#10000,4,11,4,11)
+hasLocation(#20062,#20063)
+#20064=*
+tokeninfo(#20064,8,#20001,21,"&&=")
+#20065=@"loc,{#10000},4,13,4,15"
+locations_default(#20065,#10000,4,13,4,15)
+hasLocation(#20064,#20065)
+#20066=*
+tokeninfo(#20066,6,#20001,22,"y")
+#20067=@"loc,{#10000},4,17,4,17"
+locations_default(#20067,#10000,4,17,4,17)
+hasLocation(#20066,#20067)
+#20068=*
+tokeninfo(#20068,8,#20001,23,";")
+#20069=@"loc,{#10000},4,18,4,18"
+locations_default(#20069,#10000,4,18,4,18)
+hasLocation(#20068,#20069)
+#20070=*
+tokeninfo(#20070,6,#20001,24,"console")
+#20071=@"loc,{#10000},5,1,5,7"
+locations_default(#20071,#10000,5,1,5,7)
+hasLocation(#20070,#20071)
+#20072=*
+tokeninfo(#20072,8,#20001,25,".")
+#20073=@"loc,{#10000},5,8,5,8"
+locations_default(#20073,#10000,5,8,5,8)
+hasLocation(#20072,#20073)
+#20074=*
+tokeninfo(#20074,6,#20001,26,"log")
+#20075=@"loc,{#10000},5,9,5,11"
+locations_default(#20075,#10000,5,9,5,11)
+hasLocation(#20074,#20075)
+#20076=*
+tokeninfo(#20076,8,#20001,27,"(")
+#20077=@"loc,{#10000},5,12,5,12"
+locations_default(#20077,#10000,5,12,5,12)
+hasLocation(#20076,#20077)
+#20078=*
+tokeninfo(#20078,6,#20001,28,"x")
+#20079=@"loc,{#10000},5,13,5,13"
+locations_default(#20079,#10000,5,13,5,13)
+hasLocation(#20078,#20079)
+#20080=*
+tokeninfo(#20080,8,#20001,29,")")
+#20081=@"loc,{#10000},5,14,5,14"
+locations_default(#20081,#10000,5,14,5,14)
+hasLocation(#20080,#20081)
+#20082=*
+tokeninfo(#20082,8,#20001,30,";")
+#20083=@"loc,{#10000},5,15,5,15"
+locations_default(#20083,#10000,5,15,5,15)
+hasLocation(#20082,#20083)
+#20084=*
+tokeninfo(#20084,6,#20001,31,"x")
+#20085=@"loc,{#10000},7,1,7,1"
+locations_default(#20085,#10000,7,1,7,1)
+hasLocation(#20084,#20085)
+next_token(#20002,#20084)
+#20086=*
+tokeninfo(#20086,8,#20001,32,"&&=")
+#20087=@"loc,{#10000},7,3,7,5"
+locations_default(#20087,#10000,7,3,7,5)
+hasLocation(#20086,#20087)
+#20088=*
+tokeninfo(#20088,6,#20001,33,"y")
+#20089=@"loc,{#10000},7,7,7,7"
+locations_default(#20089,#10000,7,7,7,7)
+hasLocation(#20088,#20089)
+#20090=*
+tokeninfo(#20090,8,#20001,34,";")
+#20091=@"loc,{#10000},7,8,7,8"
+locations_default(#20091,#10000,7,8,7,8)
+hasLocation(#20090,#20091)
+#20092=*
+tokeninfo(#20092,6,#20001,35,"x")
+#20093=@"loc,{#10000},8,1,8,1"
+locations_default(#20093,#10000,8,1,8,1)
+hasLocation(#20092,#20093)
+#20094=*
+tokeninfo(#20094,8,#20001,36,"||=")
+#20095=@"loc,{#10000},8,3,8,5"
+locations_default(#20095,#10000,8,3,8,5)
+hasLocation(#20094,#20095)
+#20096=*
+tokeninfo(#20096,6,#20001,37,"y")
+#20097=@"loc,{#10000},8,7,8,7"
+locations_default(#20097,#10000,8,7,8,7)
+hasLocation(#20096,#20097)
+#20098=*
+tokeninfo(#20098,8,#20001,38,";")
+#20099=@"loc,{#10000},8,8,8,8"
+locations_default(#20099,#10000,8,8,8,8)
+hasLocation(#20098,#20099)
+#20100=*
+tokeninfo(#20100,6,#20001,39,"x")
+#20101=@"loc,{#10000},9,1,9,1"
+locations_default(#20101,#10000,9,1,9,1)
+hasLocation(#20100,#20101)
+#20102=*
+tokeninfo(#20102,8,#20001,40,"??=")
+#20103=@"loc,{#10000},9,3,9,5"
+locations_default(#20103,#10000,9,3,9,5)
+hasLocation(#20102,#20103)
+#20104=*
+tokeninfo(#20104,6,#20001,41,"y")
+#20105=@"loc,{#10000},9,7,9,7"
+locations_default(#20105,#10000,9,7,9,7)
+hasLocation(#20104,#20105)
+#20106=*
+tokeninfo(#20106,8,#20001,42,";")
+#20107=@"loc,{#10000},9,8,9,8"
+locations_default(#20107,#10000,9,8,9,8)
+hasLocation(#20106,#20107)
+#20108=*
+tokeninfo(#20108,0,#20001,43,"")
+#20109=@"loc,{#10000},10,1,10,0"
+locations_default(#20109,#10000,10,1,10,0)
+hasLocation(#20108,#20109)
+toplevels(#20001,0)
+#20110=@"loc,{#10000},1,1,10,0"
+locations_default(#20110,#10000,1,1,10,0)
+hasLocation(#20001,#20110)
+#20111=@"var;{x};{#20000}"
+variables(#20111,"x",#20000)
+#20112=@"var;{y};{#20000}"
+variables(#20112,"y",#20000)
+#20113=@"var;{foo};{#20000}"
+variables(#20113,"foo",#20000)
+#20114=@"var;{bar};{#20000}"
+variables(#20114,"bar",#20000)
+#20115=*
+stmts(#20115,18,#20001,0,"var x = 1;")
+hasLocation(#20115,#20005)
+stmt_containers(#20115,#20001)
+#20116=*
+exprs(#20116,64,#20115,0,"x = 1")
+#20117=@"loc,{#10000},1,5,1,9"
+locations_default(#20117,#10000,1,5,1,9)
+hasLocation(#20116,#20117)
+enclosing_stmt(#20116,#20115)
+expr_containers(#20116,#20001)
+#20118=*
+exprs(#20118,78,#20116,0,"x")
+hasLocation(#20118,#20025)
+enclosing_stmt(#20118,#20115)
+expr_containers(#20118,#20001)
+literals("x","x",#20118)
+decl(#20118,#20111)
+#20119=*
+exprs(#20119,3,#20116,1,"1")
+hasLocation(#20119,#20029)
+enclosing_stmt(#20119,#20115)
+expr_containers(#20119,#20001)
+literals("1","1",#20119)
+#20120=*
+stmts(#20120,18,#20001,1,"var y = 2;")
+hasLocation(#20120,#20007)
+stmt_containers(#20120,#20001)
+#20121=*
+exprs(#20121,64,#20120,0,"y = 2")
+#20122=@"loc,{#10000},2,5,2,9"
+locations_default(#20122,#10000,2,5,2,9)
+hasLocation(#20121,#20122)
+enclosing_stmt(#20121,#20120)
+expr_containers(#20121,#20001)
+#20123=*
+exprs(#20123,78,#20121,0,"y")
+hasLocation(#20123,#20035)
+enclosing_stmt(#20123,#20120)
+expr_containers(#20123,#20001)
+literals("y","y",#20123)
+decl(#20123,#20112)
+#20124=*
+exprs(#20124,3,#20121,1,"2")
+hasLocation(#20124,#20039)
+enclosing_stmt(#20124,#20120)
+expr_containers(#20124,#20001)
+literals("2","2",#20124)
+#20125=*
+stmts(#20125,18,#20001,2,"var foo = x && y;")
+hasLocation(#20125,#20009)
+stmt_containers(#20125,#20001)
+#20126=*
+exprs(#20126,64,#20125,0,"foo = x && y")
+#20127=@"loc,{#10000},3,5,3,16"
+locations_default(#20127,#10000,3,5,3,16)
+hasLocation(#20126,#20127)
+enclosing_stmt(#20126,#20125)
+expr_containers(#20126,#20001)
+#20128=*
+exprs(#20128,78,#20126,0,"foo")
+hasLocation(#20128,#20045)
+enclosing_stmt(#20128,#20125)
+expr_containers(#20128,#20001)
+literals("foo","foo",#20128)
+decl(#20128,#20113)
+#20129=*
+exprs(#20129,44,#20126,1,"x && y")
+#20130=@"loc,{#10000},3,11,3,16"
+locations_default(#20130,#10000,3,11,3,16)
+hasLocation(#20129,#20130)
+enclosing_stmt(#20129,#20125)
+expr_containers(#20129,#20001)
+#20131=*
+exprs(#20131,79,#20129,0,"x")
+hasLocation(#20131,#20049)
+enclosing_stmt(#20131,#20125)
+expr_containers(#20131,#20001)
+literals("x","x",#20131)
+bind(#20131,#20111)
+#20132=*
+exprs(#20132,79,#20129,1,"y")
+hasLocation(#20132,#20053)
+enclosing_stmt(#20132,#20125)
+expr_containers(#20132,#20001)
+literals("y","y",#20132)
+bind(#20132,#20112)
+#20133=*
+stmts(#20133,18,#20001,3,"var bar = x &&= y;")
+hasLocation(#20133,#20011)
+stmt_containers(#20133,#20001)
+#20134=*
+exprs(#20134,64,#20133,0,"bar = x &&= y")
+#20135=@"loc,{#10000},4,5,4,17"
+locations_default(#20135,#10000,4,5,4,17)
+hasLocation(#20134,#20135)
+enclosing_stmt(#20134,#20133)
+expr_containers(#20134,#20001)
+#20136=*
+exprs(#20136,78,#20134,0,"bar")
+hasLocation(#20136,#20059)
+enclosing_stmt(#20136,#20133)
+expr_containers(#20136,#20001)
+literals("bar","bar",#20136)
+decl(#20136,#20114)
+#20137=*
+exprs(#20137,116,#20134,1,"x &&= y")
+#20138=@"loc,{#10000},4,11,4,17"
+locations_default(#20138,#10000,4,11,4,17)
+hasLocation(#20137,#20138)
+enclosing_stmt(#20137,#20133)
+expr_containers(#20137,#20001)
+#20139=*
+exprs(#20139,79,#20137,0,"x")
+hasLocation(#20139,#20063)
+enclosing_stmt(#20139,#20133)
+expr_containers(#20139,#20001)
+literals("x","x",#20139)
+bind(#20139,#20111)
+#20140=*
+exprs(#20140,79,#20137,1,"y")
+hasLocation(#20140,#20067)
+enclosing_stmt(#20140,#20133)
+expr_containers(#20140,#20001)
+literals("y","y",#20140)
+bind(#20140,#20112)
+#20141=*
+stmts(#20141,2,#20001,4,"console.log(x);")
+#20142=@"loc,{#10000},5,1,5,15"
+locations_default(#20142,#10000,5,1,5,15)
+hasLocation(#20141,#20142)
+stmt_containers(#20141,#20001)
+#20143=*
+exprs(#20143,13,#20141,0,"console.log(x)")
+#20144=@"loc,{#10000},5,1,5,14"
+locations_default(#20144,#10000,5,1,5,14)
+hasLocation(#20143,#20144)
+enclosing_stmt(#20143,#20141)
+expr_containers(#20143,#20001)
+#20145=*
+exprs(#20145,14,#20143,-1,"console.log")
+#20146=@"loc,{#10000},5,1,5,11"
+locations_default(#20146,#10000,5,1,5,11)
+hasLocation(#20145,#20146)
+enclosing_stmt(#20145,#20141)
+expr_containers(#20145,#20001)
+#20147=*
+exprs(#20147,79,#20145,0,"console")
+hasLocation(#20147,#20071)
+enclosing_stmt(#20147,#20141)
+expr_containers(#20147,#20001)
+literals("console","console",#20147)
+#20148=@"var;{console};{#20000}"
+variables(#20148,"console",#20000)
+bind(#20147,#20148)
+#20149=*
+exprs(#20149,0,#20145,1,"log")
+hasLocation(#20149,#20075)
+enclosing_stmt(#20149,#20141)
+expr_containers(#20149,#20001)
+literals("log","log",#20149)
+#20150=*
+exprs(#20150,79,#20143,0,"x")
+hasLocation(#20150,#20079)
+enclosing_stmt(#20150,#20141)
+expr_containers(#20150,#20001)
+literals("x","x",#20150)
+bind(#20150,#20111)
+#20151=*
+stmts(#20151,2,#20001,5,"x &&= y;")
+hasLocation(#20151,#20017)
+stmt_containers(#20151,#20001)
+#20152=*
+exprs(#20152,116,#20151,0,"x &&= y")
+#20153=@"loc,{#10000},7,1,7,7"
+locations_default(#20153,#10000,7,1,7,7)
+hasLocation(#20152,#20153)
+enclosing_stmt(#20152,#20151)
+expr_containers(#20152,#20001)
+#20154=*
+exprs(#20154,79,#20152,0,"x")
+hasLocation(#20154,#20085)
+enclosing_stmt(#20154,#20151)
+expr_containers(#20154,#20001)
+literals("x","x",#20154)
+bind(#20154,#20111)
+#20155=*
+exprs(#20155,79,#20152,1,"y")
+hasLocation(#20155,#20089)
+enclosing_stmt(#20155,#20151)
+expr_containers(#20155,#20001)
+literals("y","y",#20155)
+bind(#20155,#20112)
+#20156=*
+stmts(#20156,2,#20001,6,"x ||= y;")
+hasLocation(#20156,#20019)
+stmt_containers(#20156,#20001)
+#20157=*
+exprs(#20157,117,#20156,0,"x ||= y")
+#20158=@"loc,{#10000},8,1,8,7"
+locations_default(#20158,#10000,8,1,8,7)
+hasLocation(#20157,#20158)
+enclosing_stmt(#20157,#20156)
+expr_containers(#20157,#20001)
+#20159=*
+exprs(#20159,79,#20157,0,"x")
+hasLocation(#20159,#20093)
+enclosing_stmt(#20159,#20156)
+expr_containers(#20159,#20001)
+literals("x","x",#20159)
+bind(#20159,#20111)
+#20160=*
+exprs(#20160,79,#20157,1,"y")
+hasLocation(#20160,#20097)
+enclosing_stmt(#20160,#20156)
+expr_containers(#20160,#20001)
+literals("y","y",#20160)
+bind(#20160,#20112)
+#20161=*
+stmts(#20161,2,#20001,7,"x ??= y;")
+hasLocation(#20161,#20021)
+stmt_containers(#20161,#20001)
+#20162=*
+exprs(#20162,118,#20161,0,"x ??= y")
+#20163=@"loc,{#10000},9,1,9,7"
+locations_default(#20163,#10000,9,1,9,7)
+hasLocation(#20162,#20163)
+enclosing_stmt(#20162,#20161)
+expr_containers(#20162,#20001)
+#20164=*
+exprs(#20164,79,#20162,0,"x")
+hasLocation(#20164,#20101)
+enclosing_stmt(#20164,#20161)
+expr_containers(#20164,#20001)
+literals("x","x",#20164)
+bind(#20164,#20111)
+#20165=*
+exprs(#20165,79,#20162,1,"y")
+hasLocation(#20165,#20105)
+enclosing_stmt(#20165,#20161)
+expr_containers(#20165,#20001)
+literals("y","y",#20165)
+bind(#20165,#20112)
+#20166=*
+entry_cfg_node(#20166,#20001)
+#20167=@"loc,{#10000},1,1,1,0"
+locations_default(#20167,#10000,1,1,1,0)
+hasLocation(#20166,#20167)
+#20168=*
+exit_cfg_node(#20168,#20001)
+hasLocation(#20168,#20109)
+successor(#20161,#20164)
+successor(#20164,#20165)
+successor(#20164,#20168)
+successor(#20165,#20164)
+successor(#20162,#20168)
+successor(#20156,#20159)
+#20169=*
+guard_node(#20169,1,#20159)
+hasLocation(#20169,#20093)
+successor(#20169,#20161)
+#20170=*
+guard_node(#20170,0,#20159)
+hasLocation(#20170,#20093)
+successor(#20170,#20160)
+successor(#20159,#20169)
+successor(#20159,#20170)
+successor(#20160,#20159)
+successor(#20157,#20161)
+successor(#20151,#20154)
+#20171=*
+guard_node(#20171,1,#20154)
+hasLocation(#20171,#20085)
+successor(#20171,#20155)
+#20172=*
+guard_node(#20172,0,#20154)
+hasLocation(#20172,#20085)
+successor(#20172,#20156)
+successor(#20154,#20171)
+successor(#20154,#20172)
+successor(#20155,#20154)
+successor(#20152,#20156)
+successor(#20141,#20147)
+successor(#20150,#20143)
+successor(#20149,#20145)
+successor(#20147,#20149)
+successor(#20145,#20150)
+successor(#20143,#20151)
+successor(#20133,#20136)
+#20173=*
+guard_node(#20173,1,#20139)
+hasLocation(#20173,#20063)
+successor(#20173,#20140)
+#20174=*
+guard_node(#20174,0,#20139)
+hasLocation(#20174,#20063)
+successor(#20174,#20134)
+successor(#20139,#20173)
+successor(#20139,#20174)
+successor(#20140,#20139)
+successor(#20137,#20134)
+successor(#20136,#20139)
+successor(#20134,#20141)
+successor(#20125,#20128)
+successor(#20129,#20131)
+#20175=*
+guard_node(#20175,1,#20131)
+hasLocation(#20175,#20049)
+successor(#20175,#20132)
+#20176=*
+guard_node(#20176,0,#20131)
+hasLocation(#20176,#20049)
+successor(#20176,#20126)
+successor(#20131,#20175)
+successor(#20131,#20176)
+successor(#20132,#20126)
+successor(#20128,#20129)
+successor(#20126,#20133)
+successor(#20120,#20123)
+successor(#20124,#20121)
+successor(#20123,#20124)
+successor(#20121,#20125)
+successor(#20115,#20118)
+successor(#20119,#20116)
+successor(#20118,#20119)
+successor(#20116,#20120)
+successor(#20166,#20115)
+numlines(#10000,9,8,1)
+filetype(#10000,"javascript")
From 0dbdbfa6596a026c4403635b8ad0dac98a80e73e Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 17 Sep 2020 13:13:55 +0200
Subject: [PATCH 039/185] bump extractor version
---
javascript/extractor/src/com/semmle/js/extractor/Main.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java
index 359e07d3194..a7b209bc7ce 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/Main.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java
@@ -43,7 +43,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
- public static final String EXTRACTOR_VERSION = "2020-09-12";
+ public static final String EXTRACTOR_VERSION = "2020-09-17";
public static final Pattern NEWLINE = Pattern.compile("\n");
From b09015380ac778d9376bf54a5f253af064bb1813 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 17 Sep 2020 14:00:44 +0200
Subject: [PATCH 040/185] add support for String.prototype.replaceAll
---
.../ql/src/Expressions/StringInsteadOfRegex.ql | 2 +-
.../ql/src/RegExp/IdentityReplacement.ql | 2 +-
.../CWE-020/IncompleteUrlSchemeCheck.ql | 2 +-
.../Security/CWE-116/IncompleteSanitization.ql | 1 +
.../src/semmle/javascript/StandardLibrary.qll | 6 +++---
.../javascript/dataflow/TaintTracking.qll | 6 ++++--
.../heuristics/AdditionalTaintSteps.qll | 4 +---
.../CleartextLoggingCustomizations.qll | 12 ++++--------
.../dataflow/TaintedPathCustomizations.qll | 18 ++++++++----------
.../security/dataflow/UnsafeJQueryPlugin.qll | 5 ++---
.../javascript/security/dataflow/Xss.qll | 5 ++---
.../PolynomialReDoSCustomizations.qll | 1 +
.../InterProceduralFlow/TaintTracking.expected | 2 ++
.../library-tests/InterProceduralFlow/tst.js | 3 +++
.../ReDoS/PolynomialBackTracking.expected | 1 +
.../Performance/ReDoS/PolynomialReDoS.expected | 5 +++++
.../Performance/ReDoS/polynomial-redos.js | 2 +-
17 files changed, 41 insertions(+), 36 deletions(-)
diff --git a/javascript/ql/src/Expressions/StringInsteadOfRegex.ql b/javascript/ql/src/Expressions/StringInsteadOfRegex.ql
index f81b8b1a5ae..f1efb37e330 100644
--- a/javascript/ql/src/Expressions/StringInsteadOfRegex.ql
+++ b/javascript/ql/src/Expressions/StringInsteadOfRegex.ql
@@ -31,7 +31,7 @@ predicate isStringSplitOrReplace(MethodCallExpr mce) {
mce.getMethodName() = name and
mce.getNumArgument() = arity
|
- name = "replace" and arity = 2
+ name = ["replace", "replaceAll"] and arity = 2
or
name = "split" and
(arity = 1 or arity = 2)
diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql
index f8c62a1ab3a..2a6e354cc9c 100644
--- a/javascript/ql/src/RegExp/IdentityReplacement.ql
+++ b/javascript/ql/src/RegExp/IdentityReplacement.ql
@@ -70,7 +70,7 @@ predicate regExpMatchesString(RegExpTerm t, string s) {
from MethodCallExpr repl, string s, string friendly
where
- repl.getMethodName() = "replace" and
+ repl.getMethodName() = ["replace", "replaceAll"] and
matchesString(repl.getArgument(0), s) and
repl.getArgument(1).getStringValue() = s and
(if s = "" then friendly = "the empty string" else friendly = "'" + s + "'")
diff --git a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql
index 617692f3adb..0675b189cd1 100644
--- a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql
+++ b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql
@@ -75,7 +75,7 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) {
exists(DataFlow::MethodCallNode stringop |
stringop.getMethodName().matches("trim%") or
stringop.getMethodName().matches("to%Case") or
- stringop.getMethodName() = "replace"
+ stringop.getMethodName() = ["replace", "replaceAll"]
|
result = schemeCheck(stringop, scheme) and
nd = stringop.getReceiver()
diff --git a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql
index a1b466cab9f..9494dcde0e5 100644
--- a/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql
+++ b/javascript/ql/src/Security/CWE-116/IncompleteSanitization.ql
@@ -79,6 +79,7 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
// flow through string methods
exists(DataFlow::MethodCallNode mc, string m |
m = "replace" or
+ m = "replaceAll" or
m = "slice" or
m = "substr" or
m = "substring" or
diff --git a/javascript/ql/src/semmle/javascript/StandardLibrary.qll b/javascript/ql/src/semmle/javascript/StandardLibrary.qll
index b28d1f27439..6a57bda6461 100644
--- a/javascript/ql/src/semmle/javascript/StandardLibrary.qll
+++ b/javascript/ql/src/semmle/javascript/StandardLibrary.qll
@@ -102,7 +102,7 @@ private class IteratorExceptionStep extends DataFlow::MethodCallNode, DataFlow::
*/
class StringReplaceCall extends DataFlow::MethodCallNode {
StringReplaceCall() {
- getMethodName() = "replace" and
+ getMethodName() = ["replace", "replaceAll"] and
(getNumArgument() = 2 or getReceiver().mayHaveStringValue(_))
}
@@ -128,9 +128,9 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
/**
* Holds if this is a global replacement, that is, the first argument is a regular expression
- * with the `g` flag.
+ * with the `g` flag, or this is a call to `.replaceAll()`.
*/
- predicate isGlobal() { getRegExp().isGlobal() }
+ predicate isGlobal() { getRegExp().isGlobal() or getMethodName() = "replaceAll" }
/**
* Holds if this call to `replace` replaces `old` with `new`.
diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll
index f0763c0893f..6698800918c 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll
@@ -427,6 +427,7 @@ module TaintTracking {
name = "padStart" or
name = "repeat" or
name = "replace" or
+ name = "replaceAll" or
name = "slice" or
name = "small" or
name = "split" or
@@ -452,7 +453,7 @@ module TaintTracking {
exists(int i | pred.asExpr() = succ.getAstNode().(MethodCallExpr).getArgument(i) |
name = "concat"
or
- name = "replace" and i = 1
+ name = ["replace", "replaceAll"] and i = 1
)
)
or
@@ -707,7 +708,8 @@ module TaintTracking {
*/
private ControlFlowNode getACaptureSetter(DataFlow::Node input) {
exists(DataFlow::MethodCallNode call | result = call.asExpr() |
- call.getMethodName() = ["search", "replace", "match"] and input = call.getReceiver()
+ call.getMethodName() = ["search", "replace", "replaceAll", "match"] and
+ input = call.getReceiver()
or
call.getMethodName() = ["test", "exec"] and input = call.getArgument(0)
)
diff --git a/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll b/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll
index e920fb10613..5cda6f0d867 100644
--- a/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll
+++ b/javascript/ql/src/semmle/javascript/heuristics/AdditionalTaintSteps.qll
@@ -15,9 +15,7 @@ abstract class HeuristicAdditionalTaintStep extends DataFlow::ValueNode { }
* A call to `tainted.replace(x, y)` that preserves taint.
*/
private class HeuristicStringManipulationTaintStep extends HeuristicAdditionalTaintStep,
- TaintTracking::AdditionalTaintStep, DataFlow::MethodCallNode {
- HeuristicStringManipulationTaintStep() { getMethodName() = "replace" }
-
+ TaintTracking::AdditionalTaintStep, StringReplaceCall {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getReceiver() and succ = this
}
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll
index 6fc13631ef2..eea099a7a79 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll
@@ -34,15 +34,11 @@ module CleartextLogging {
/**
* A call to `.replace()` that seems to mask sensitive information.
*/
- class MaskingReplacer extends Barrier, DataFlow::MethodCallNode {
+ class MaskingReplacer extends Barrier, StringReplaceCall {
MaskingReplacer() {
- this.getCalleeName() = "replace" and
- exists(RegExpLiteral reg |
- reg = this.getArgument(0).getALocalSource().asExpr() and
- reg.isGlobal() and
- any(RegExpDot term).getLiteral() = reg
- ) and
- exists(this.getArgument(1).getStringValue())
+ this.isGlobal() and
+ exists(this.getRawReplacement().getStringValue()) and
+ any(RegExpDot term).getLiteral() = getRegExp().asExpr()
}
}
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
index daf0e9324ee..76b9e350427 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll
@@ -218,12 +218,12 @@ module TaintedPath {
output = this
or
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
- this.getCalleeName() = "replace" and
+ this instanceof StringReplaceCall and
input = getReceiver() and
output = this and
not exists(RegExpLiteral literal, RegExpTerm term |
- getArgument(0).getALocalSource().asExpr() = literal and
- literal.isGlobal() and
+ this.(StringReplaceCall).getRegExp().asExpr() = literal and
+ this.(StringReplaceCall).isGlobal() and
literal.getRoot() = term
|
term.getAMatchedString() = "/" or
@@ -247,16 +247,15 @@ module TaintedPath {
/**
* A call that removes all instances of "../" in the prefix of the string.
*/
- class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
+ class DotDotSlashPrefixRemovingReplace extends StringReplaceCall {
DataFlow::Node input;
DataFlow::Node output;
DotDotSlashPrefixRemovingReplace() {
- this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
exists(RegExpLiteral literal, RegExpTerm term |
- getArgument(0).getALocalSource().asExpr() = literal and
+ getRegExp().asExpr() = literal and
(term instanceof RegExpStar or term instanceof RegExpPlus) and
term.getChild(0) = getADotDotSlashMatcher()
|
@@ -298,17 +297,16 @@ module TaintedPath {
/**
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
*/
- class DotRemovingReplaceCall extends DataFlow::CallNode {
+ class DotRemovingReplaceCall extends StringReplaceCall {
DataFlow::Node input;
DataFlow::Node output;
DotRemovingReplaceCall() {
- this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
+ isGlobal() and
exists(RegExpLiteral literal, RegExpTerm term |
- getArgument(0).getALocalSource().asExpr() = literal and
- literal.isGlobal() and
+ getRegExp().asExpr() = literal and
literal.getRoot() = term and
not term.getAMatchedString() = "/"
|
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll
index a2a7246b4a7..05c33b71330 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeJQueryPlugin.qll
@@ -39,10 +39,9 @@ module UnsafeJQueryPlugin {
StringConcatenation::taintStep(pred, succ, _, any(int i | i >= 1))
or
// prefixing through a poor-mans templating system:
- exists(DataFlow::MethodCallNode replace |
+ exists(StringReplaceCall replace |
replace = succ and
- pred = replace.getArgument(1) and
- replace.getMethodName() = "replace"
+ pred = replace.getRawReplacement()
)
}
diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll
index 54e7b7de47d..346ff52c862 100644
--- a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll
+++ b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll
@@ -33,11 +33,10 @@ module Shared {
* The XSS queries do not attempt to reason about correctness or completeness of sanitizers,
* so any such replacement stops taint propagation.
*/
- class MetacharEscapeSanitizer extends Sanitizer, DataFlow::MethodCallNode {
+ class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall {
MetacharEscapeSanitizer() {
- getMethodName() = "replace" and
exists(RegExpConstant c |
- c.getLiteral() = getArgument(0).getALocalSource().asExpr() and
+ c.getLiteral() = getRegExp().asExpr() and
c.getValue().regexpMatch("['\"&<>]")
)
}
diff --git a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
index 5507f9e6981..ea3d9db3ada 100644
--- a/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
+++ b/javascript/ql/src/semmle/javascript/security/performance/PolynomialReDoSCustomizations.qll
@@ -51,6 +51,7 @@ module PolynomialReDoS {
name = "split" or
name = "matchAll" or
name = "replace" or
+ name = "replaceAll" or
name = "search"
)
or
diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected b/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected
index c3ea1cdb5f2..823db35f84d 100644
--- a/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected
+++ b/javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected
@@ -86,6 +86,8 @@
| tst.js:2:17:2:22 | "src1" | tst.js:61:16:61:18 | o.r |
| tst.js:2:17:2:22 | "src1" | tst.js:68:16:68:22 | inner() |
| tst.js:2:17:2:22 | "src1" | tst.js:80:16:80:22 | outer() |
+| tst.js:2:17:2:22 | "src1" | tst.js:87:16:87:43 | source1 ... /g, "") |
+| tst.js:2:17:2:22 | "src1" | tst.js:88:16:88:46 | "foo".r ... ource1) |
| underscore.js:2:17:2:22 | "src1" | underscore.js:3:15:3:28 | _.max(source1) |
| underscore.js:5:17:5:22 | "src2" | underscore.js:6:15:6:34 | _.union([], source2) |
| underscore.js:5:17:5:22 | "src2" | underscore.js:7:15:7:32 | _.zip(source2, []) |
diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/tst.js b/javascript/ql/test/library-tests/InterProceduralFlow/tst.js
index a557538bd11..a8fc56e3d52 100644
--- a/javascript/ql/test/library-tests/InterProceduralFlow/tst.js
+++ b/javascript/ql/test/library-tests/InterProceduralFlow/tst.js
@@ -83,4 +83,7 @@
o.notTracked = source1;
var sink22 = o.notTracked;
+
+ var sink23 = source1.replaceAll(/f/g, "");
+ var sink24 = "foo".replaceAll(/f/g, source1);
})();
diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected
index 98f5cb00ed4..1d78b51e8bf 100644
--- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected
+++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected
@@ -17,6 +17,7 @@
| polynomial-redos.js:31:42:31:43 | -+ | it can start matching anywhere |
| polynomial-redos.js:32:45:32:47 | \\n* | it can start matching anywhere |
| polynomial-redos.js:33:17:33:20 | (.)* | it can start matching anywhere |
+| polynomial-redos.js:48:22:48:24 | \\s* | it can start matching anywhere |
| regexplib/address.js:18:26:18:31 | [ \\w]* | it can start matching anywhere after the start of the preceeding '[ \\w]{3,}' |
| regexplib/address.js:20:144:20:147 | [ ]+ | it can start matching anywhere after the start of the preceeding '[a-zA-Z0-9 \\-.]{6,}' |
| regexplib/address.js:24:26:24:31 | [ \\w]* | it can start matching anywhere after the start of the preceeding '[ \\w]{3,}' |
diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected
index 4d3a9a12da6..e8ba1a56d67 100644
--- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected
+++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected
@@ -28,6 +28,8 @@ nodes
| polynomial-redos.js:30:2:30:8 | tainted |
| polynomial-redos.js:33:2:33:8 | tainted |
| polynomial-redos.js:33:2:33:8 | tainted |
+| polynomial-redos.js:48:2:48:8 | tainted |
+| polynomial-redos.js:48:2:48:8 | tainted |
edges
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted |
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:7:2:7:8 | tainted |
@@ -55,6 +57,8 @@ edges
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:30:2:30:8 | tainted |
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:33:2:33:8 | tainted |
| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:33:2:33:8 | tainted |
+| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:48:2:48:8 | tainted |
+| polynomial-redos.js:5:6:5:32 | tainted | polynomial-redos.js:48:2:48:8 | tainted |
| polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted |
| polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:5:6:5:32 | tainted |
#select
@@ -72,3 +76,4 @@ edges
| polynomial-redos.js:27:77:27:83 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:27:77:27:83 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:27:14:27:22 | [A-Z]{2,} | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
| polynomial-redos.js:30:2:30:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:30:2:30:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:30:19:30:22 | [?]+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
| polynomial-redos.js:33:2:33:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:33:2:33:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:33:17:33:20 | (.)* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
+| polynomial-redos.js:48:2:48:8 | tainted | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:48:2:48:8 | tainted | This expensive $@ use depends on $@. | polynomial-redos.js:48:22:48:24 | \\s* | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value |
diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js b/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js
index 4175bd48a50..1dcce05f3c7 100644
--- a/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js
+++ b/javascript/ql/test/query-tests/Performance/ReDoS/polynomial-redos.js
@@ -45,5 +45,5 @@ app.use(function(req, res) {
tainted.match(/^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)+$/); // NOT OK - but not flagged
tainted.match(/^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*$/); // OK
-
+ tainted.replaceAll(/\s*\n\s*/g, ' '); // NOT OK
});
From 9f1b3d61b9fa6a87969cfedf4bdf38e3388a19a6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 17 Sep 2020 14:04:07 +0200
Subject: [PATCH 041/185] add test for numeric separators
---
.../extractor/tests/es2021/input/numeric.js | 8 +
.../tests/es2021/output/trap/numeric.js.trap | 435 ++++++++++++++++++
2 files changed, 443 insertions(+)
create mode 100644 javascript/extractor/tests/es2021/input/numeric.js
create mode 100644 javascript/extractor/tests/es2021/output/trap/numeric.js.trap
diff --git a/javascript/extractor/tests/es2021/input/numeric.js b/javascript/extractor/tests/es2021/input/numeric.js
new file mode 100644
index 00000000000..e7e9d031f50
--- /dev/null
+++ b/javascript/extractor/tests/es2021/input/numeric.js
@@ -0,0 +1,8 @@
+1_000_000_000 // Ah, so a billion
+101_475_938.38 // And this is hundreds of millions
+
+let fee = 123_00; // $123 (12300 cents, apparently)
+let fee = 12_300; // $12,300 (woah, that fee!)
+let amount = 12345_00; // 12,345 (1234500 cents, apparently)
+let amount = 123_4500; // 123.45 (4-fixed financial)
+let amount = 1_234_500; // 1,234,500
diff --git a/javascript/extractor/tests/es2021/output/trap/numeric.js.trap b/javascript/extractor/tests/es2021/output/trap/numeric.js.trap
new file mode 100644
index 00000000000..afcf1627f2d
--- /dev/null
+++ b/javascript/extractor/tests/es2021/output/trap/numeric.js.trap
@@ -0,0 +1,435 @@
+#10000=@"/numeric.js;sourcefile"
+files(#10000,"/numeric.js","numeric","js",0)
+#10001=@"/;folder"
+folders(#10001,"/","")
+containerparent(#10001,#10000)
+#10002=@"loc,{#10000},0,0,0,0"
+locations_default(#10002,#10000,0,0,0,0)
+hasLocation(#10000,#10002)
+#20000=@"global_scope"
+scopes(#20000,0)
+#20001=@"script;{#10000},1,1"
+#20002=*
+comments(#20002,0,#20001," Ah, so a billion","// Ah, so a billion")
+#20003=@"loc,{#10000},1,25,1,43"
+locations_default(#20003,#10000,1,25,1,43)
+hasLocation(#20002,#20003)
+#20004=*
+comments(#20004,0,#20001," And this is hundreds of millions","// And ... illions")
+#20005=@"loc,{#10000},2,25,2,59"
+locations_default(#20005,#10000,2,25,2,59)
+hasLocation(#20004,#20005)
+#20006=*
+comments(#20006,0,#20001," $123 (12300 cents, apparently)","// $123 ... rently)")
+#20007=@"loc,{#10000},4,25,4,57"
+locations_default(#20007,#10000,4,25,4,57)
+hasLocation(#20006,#20007)
+#20008=*
+comments(#20008,0,#20001," $12,300 (woah, that fee!)","// $12, ... t fee!)")
+#20009=@"loc,{#10000},5,25,5,52"
+locations_default(#20009,#10000,5,25,5,52)
+hasLocation(#20008,#20009)
+#20010=*
+comments(#20010,0,#20001," 12,345 (1234500 cents, apparently)","// 12,3 ... rently)")
+#20011=@"loc,{#10000},6,25,6,61"
+locations_default(#20011,#10000,6,25,6,61)
+hasLocation(#20010,#20011)
+#20012=*
+comments(#20012,0,#20001," 123.45 (4-fixed financial)","// 123. ... ancial)")
+#20013=@"loc,{#10000},7,25,7,53"
+locations_default(#20013,#10000,7,25,7,53)
+hasLocation(#20012,#20013)
+#20014=*
+comments(#20014,0,#20001," 1,234,500","// 1,234,500")
+#20015=@"loc,{#10000},8,25,8,36"
+locations_default(#20015,#10000,8,25,8,36)
+hasLocation(#20014,#20015)
+#20016=*
+lines(#20016,#20001,"1_000_000_000 // Ah, so a billion","
+")
+#20017=@"loc,{#10000},1,1,1,43"
+locations_default(#20017,#10000,1,1,1,43)
+hasLocation(#20016,#20017)
+#20018=*
+lines(#20018,#20001,"101_475_938.38 // And this is hundreds of millions","
+")
+#20019=@"loc,{#10000},2,1,2,59"
+locations_default(#20019,#10000,2,1,2,59)
+hasLocation(#20018,#20019)
+#20020=*
+lines(#20020,#20001,"","
+")
+#20021=@"loc,{#10000},3,1,3,0"
+locations_default(#20021,#10000,3,1,3,0)
+hasLocation(#20020,#20021)
+#20022=*
+lines(#20022,#20001,"let fee = 123_00; // $123 (12300 cents, apparently)","
+")
+#20023=@"loc,{#10000},4,1,4,57"
+locations_default(#20023,#10000,4,1,4,57)
+hasLocation(#20022,#20023)
+#20024=*
+lines(#20024,#20001,"let fee = 12_300; // $12,300 (woah, that fee!)","
+")
+#20025=@"loc,{#10000},5,1,5,52"
+locations_default(#20025,#10000,5,1,5,52)
+hasLocation(#20024,#20025)
+#20026=*
+lines(#20026,#20001,"let amount = 12345_00; // 12,345 (1234500 cents, apparently)","
+")
+#20027=@"loc,{#10000},6,1,6,61"
+locations_default(#20027,#10000,6,1,6,61)
+hasLocation(#20026,#20027)
+#20028=*
+lines(#20028,#20001,"let amount = 123_4500; // 123.45 (4-fixed financial)","
+")
+#20029=@"loc,{#10000},7,1,7,53"
+locations_default(#20029,#10000,7,1,7,53)
+hasLocation(#20028,#20029)
+#20030=*
+lines(#20030,#20001,"let amount = 1_234_500; // 1,234,500","
+")
+#20031=@"loc,{#10000},8,1,8,36"
+locations_default(#20031,#10000,8,1,8,36)
+hasLocation(#20030,#20031)
+numlines(#20001,8,7,7)
+#20032=*
+tokeninfo(#20032,3,#20001,0,"1_000_000_000")
+#20033=@"loc,{#10000},1,1,1,13"
+locations_default(#20033,#10000,1,1,1,13)
+hasLocation(#20032,#20033)
+#20034=*
+tokeninfo(#20034,3,#20001,1,"101_475_938.38")
+#20035=@"loc,{#10000},2,1,2,14"
+locations_default(#20035,#10000,2,1,2,14)
+hasLocation(#20034,#20035)
+next_token(#20002,#20034)
+#20036=*
+tokeninfo(#20036,7,#20001,2,"let")
+#20037=@"loc,{#10000},4,1,4,3"
+locations_default(#20037,#10000,4,1,4,3)
+hasLocation(#20036,#20037)
+next_token(#20004,#20036)
+#20038=*
+tokeninfo(#20038,6,#20001,3,"fee")
+#20039=@"loc,{#10000},4,5,4,7"
+locations_default(#20039,#10000,4,5,4,7)
+hasLocation(#20038,#20039)
+#20040=*
+tokeninfo(#20040,8,#20001,4,"=")
+#20041=@"loc,{#10000},4,9,4,9"
+locations_default(#20041,#10000,4,9,4,9)
+hasLocation(#20040,#20041)
+#20042=*
+tokeninfo(#20042,3,#20001,5,"123_00")
+#20043=@"loc,{#10000},4,11,4,16"
+locations_default(#20043,#10000,4,11,4,16)
+hasLocation(#20042,#20043)
+#20044=*
+tokeninfo(#20044,8,#20001,6,";")
+#20045=@"loc,{#10000},4,17,4,17"
+locations_default(#20045,#10000,4,17,4,17)
+hasLocation(#20044,#20045)
+#20046=*
+tokeninfo(#20046,7,#20001,7,"let")
+#20047=@"loc,{#10000},5,1,5,3"
+locations_default(#20047,#10000,5,1,5,3)
+hasLocation(#20046,#20047)
+next_token(#20006,#20046)
+#20048=*
+tokeninfo(#20048,6,#20001,8,"fee")
+#20049=@"loc,{#10000},5,5,5,7"
+locations_default(#20049,#10000,5,5,5,7)
+hasLocation(#20048,#20049)
+#20050=*
+tokeninfo(#20050,8,#20001,9,"=")
+#20051=@"loc,{#10000},5,9,5,9"
+locations_default(#20051,#10000,5,9,5,9)
+hasLocation(#20050,#20051)
+#20052=*
+tokeninfo(#20052,3,#20001,10,"12_300")
+#20053=@"loc,{#10000},5,11,5,16"
+locations_default(#20053,#10000,5,11,5,16)
+hasLocation(#20052,#20053)
+#20054=*
+tokeninfo(#20054,8,#20001,11,";")
+#20055=@"loc,{#10000},5,17,5,17"
+locations_default(#20055,#10000,5,17,5,17)
+hasLocation(#20054,#20055)
+#20056=*
+tokeninfo(#20056,7,#20001,12,"let")
+#20057=@"loc,{#10000},6,1,6,3"
+locations_default(#20057,#10000,6,1,6,3)
+hasLocation(#20056,#20057)
+next_token(#20008,#20056)
+#20058=*
+tokeninfo(#20058,6,#20001,13,"amount")
+#20059=@"loc,{#10000},6,5,6,10"
+locations_default(#20059,#10000,6,5,6,10)
+hasLocation(#20058,#20059)
+#20060=*
+tokeninfo(#20060,8,#20001,14,"=")
+#20061=@"loc,{#10000},6,12,6,12"
+locations_default(#20061,#10000,6,12,6,12)
+hasLocation(#20060,#20061)
+#20062=*
+tokeninfo(#20062,3,#20001,15,"12345_00")
+#20063=@"loc,{#10000},6,14,6,21"
+locations_default(#20063,#10000,6,14,6,21)
+hasLocation(#20062,#20063)
+#20064=*
+tokeninfo(#20064,8,#20001,16,";")
+#20065=@"loc,{#10000},6,22,6,22"
+locations_default(#20065,#10000,6,22,6,22)
+hasLocation(#20064,#20065)
+#20066=*
+tokeninfo(#20066,7,#20001,17,"let")
+#20067=@"loc,{#10000},7,1,7,3"
+locations_default(#20067,#10000,7,1,7,3)
+hasLocation(#20066,#20067)
+next_token(#20010,#20066)
+#20068=*
+tokeninfo(#20068,6,#20001,18,"amount")
+#20069=@"loc,{#10000},7,5,7,10"
+locations_default(#20069,#10000,7,5,7,10)
+hasLocation(#20068,#20069)
+#20070=*
+tokeninfo(#20070,8,#20001,19,"=")
+#20071=@"loc,{#10000},7,12,7,12"
+locations_default(#20071,#10000,7,12,7,12)
+hasLocation(#20070,#20071)
+#20072=*
+tokeninfo(#20072,3,#20001,20,"123_4500")
+#20073=@"loc,{#10000},7,14,7,21"
+locations_default(#20073,#10000,7,14,7,21)
+hasLocation(#20072,#20073)
+#20074=*
+tokeninfo(#20074,8,#20001,21,";")
+#20075=@"loc,{#10000},7,22,7,22"
+locations_default(#20075,#10000,7,22,7,22)
+hasLocation(#20074,#20075)
+#20076=*
+tokeninfo(#20076,7,#20001,22,"let")
+#20077=@"loc,{#10000},8,1,8,3"
+locations_default(#20077,#10000,8,1,8,3)
+hasLocation(#20076,#20077)
+next_token(#20012,#20076)
+#20078=*
+tokeninfo(#20078,6,#20001,23,"amount")
+#20079=@"loc,{#10000},8,5,8,10"
+locations_default(#20079,#10000,8,5,8,10)
+hasLocation(#20078,#20079)
+#20080=*
+tokeninfo(#20080,8,#20001,24,"=")
+#20081=@"loc,{#10000},8,12,8,12"
+locations_default(#20081,#10000,8,12,8,12)
+hasLocation(#20080,#20081)
+#20082=*
+tokeninfo(#20082,3,#20001,25,"1_234_500")
+#20083=@"loc,{#10000},8,14,8,22"
+locations_default(#20083,#10000,8,14,8,22)
+hasLocation(#20082,#20083)
+#20084=*
+tokeninfo(#20084,8,#20001,26,";")
+#20085=@"loc,{#10000},8,23,8,23"
+locations_default(#20085,#10000,8,23,8,23)
+hasLocation(#20084,#20085)
+#20086=*
+tokeninfo(#20086,0,#20001,27,"")
+#20087=@"loc,{#10000},9,1,9,0"
+locations_default(#20087,#10000,9,1,9,0)
+hasLocation(#20086,#20087)
+next_token(#20014,#20086)
+toplevels(#20001,0)
+#20088=@"loc,{#10000},1,1,9,0"
+locations_default(#20088,#10000,1,1,9,0)
+hasLocation(#20001,#20088)
+#20089=@"var;{fee};{#20000}"
+variables(#20089,"fee",#20000)
+#20090=@"var;{amount};{#20000}"
+variables(#20090,"amount",#20000)
+#20091=*
+stmts(#20091,2,#20001,0,"1_000_000_000")
+hasLocation(#20091,#20033)
+stmt_containers(#20091,#20001)
+#20092=*
+exprs(#20092,3,#20091,0,"1_000_000_000")
+hasLocation(#20092,#20033)
+enclosing_stmt(#20092,#20091)
+expr_containers(#20092,#20001)
+literals("1000000000","1_000_000_000",#20092)
+#20093=*
+stmts(#20093,2,#20001,1,"101_475_938.38")
+hasLocation(#20093,#20035)
+stmt_containers(#20093,#20001)
+#20094=*
+exprs(#20094,3,#20093,0,"101_475_938.38")
+hasLocation(#20094,#20035)
+enclosing_stmt(#20094,#20093)
+expr_containers(#20094,#20001)
+literals("1.0147593838E8","101_475_938.38",#20094)
+#20095=*
+stmts(#20095,23,#20001,2,"let fee = 123_00;")
+#20096=@"loc,{#10000},4,1,4,17"
+locations_default(#20096,#10000,4,1,4,17)
+hasLocation(#20095,#20096)
+stmt_containers(#20095,#20001)
+#20097=*
+exprs(#20097,64,#20095,0,"fee = 123_00")
+#20098=@"loc,{#10000},4,5,4,16"
+locations_default(#20098,#10000,4,5,4,16)
+hasLocation(#20097,#20098)
+enclosing_stmt(#20097,#20095)
+expr_containers(#20097,#20001)
+#20099=*
+exprs(#20099,78,#20097,0,"fee")
+hasLocation(#20099,#20039)
+enclosing_stmt(#20099,#20095)
+expr_containers(#20099,#20001)
+literals("fee","fee",#20099)
+decl(#20099,#20089)
+#20100=*
+exprs(#20100,3,#20097,1,"123_00")
+hasLocation(#20100,#20043)
+enclosing_stmt(#20100,#20095)
+expr_containers(#20100,#20001)
+literals("12300","123_00",#20100)
+#20101=*
+stmts(#20101,23,#20001,3,"let fee = 12_300;")
+#20102=@"loc,{#10000},5,1,5,17"
+locations_default(#20102,#10000,5,1,5,17)
+hasLocation(#20101,#20102)
+stmt_containers(#20101,#20001)
+#20103=*
+exprs(#20103,64,#20101,0,"fee = 12_300")
+#20104=@"loc,{#10000},5,5,5,16"
+locations_default(#20104,#10000,5,5,5,16)
+hasLocation(#20103,#20104)
+enclosing_stmt(#20103,#20101)
+expr_containers(#20103,#20001)
+#20105=*
+exprs(#20105,78,#20103,0,"fee")
+hasLocation(#20105,#20049)
+enclosing_stmt(#20105,#20101)
+expr_containers(#20105,#20001)
+literals("fee","fee",#20105)
+decl(#20105,#20089)
+#20106=*
+exprs(#20106,3,#20103,1,"12_300")
+hasLocation(#20106,#20053)
+enclosing_stmt(#20106,#20101)
+expr_containers(#20106,#20001)
+literals("12300","12_300",#20106)
+#20107=*
+stmts(#20107,23,#20001,4,"let amo ... 345_00;")
+#20108=@"loc,{#10000},6,1,6,22"
+locations_default(#20108,#10000,6,1,6,22)
+hasLocation(#20107,#20108)
+stmt_containers(#20107,#20001)
+#20109=*
+exprs(#20109,64,#20107,0,"amount = 12345_00")
+#20110=@"loc,{#10000},6,5,6,21"
+locations_default(#20110,#10000,6,5,6,21)
+hasLocation(#20109,#20110)
+enclosing_stmt(#20109,#20107)
+expr_containers(#20109,#20001)
+#20111=*
+exprs(#20111,78,#20109,0,"amount")
+hasLocation(#20111,#20059)
+enclosing_stmt(#20111,#20107)
+expr_containers(#20111,#20001)
+literals("amount","amount",#20111)
+decl(#20111,#20090)
+#20112=*
+exprs(#20112,3,#20109,1,"12345_00")
+hasLocation(#20112,#20063)
+enclosing_stmt(#20112,#20107)
+expr_containers(#20112,#20001)
+literals("1234500","12345_00",#20112)
+#20113=*
+stmts(#20113,23,#20001,5,"let amo ... 3_4500;")
+#20114=@"loc,{#10000},7,1,7,22"
+locations_default(#20114,#10000,7,1,7,22)
+hasLocation(#20113,#20114)
+stmt_containers(#20113,#20001)
+#20115=*
+exprs(#20115,64,#20113,0,"amount = 123_4500")
+#20116=@"loc,{#10000},7,5,7,21"
+locations_default(#20116,#10000,7,5,7,21)
+hasLocation(#20115,#20116)
+enclosing_stmt(#20115,#20113)
+expr_containers(#20115,#20001)
+#20117=*
+exprs(#20117,78,#20115,0,"amount")
+hasLocation(#20117,#20069)
+enclosing_stmt(#20117,#20113)
+expr_containers(#20117,#20001)
+literals("amount","amount",#20117)
+decl(#20117,#20090)
+#20118=*
+exprs(#20118,3,#20115,1,"123_4500")
+hasLocation(#20118,#20073)
+enclosing_stmt(#20118,#20113)
+expr_containers(#20118,#20001)
+literals("1234500","123_4500",#20118)
+#20119=*
+stmts(#20119,23,#20001,6,"let amo ... 34_500;")
+#20120=@"loc,{#10000},8,1,8,23"
+locations_default(#20120,#10000,8,1,8,23)
+hasLocation(#20119,#20120)
+stmt_containers(#20119,#20001)
+#20121=*
+exprs(#20121,64,#20119,0,"amount = 1_234_500")
+#20122=@"loc,{#10000},8,5,8,22"
+locations_default(#20122,#10000,8,5,8,22)
+hasLocation(#20121,#20122)
+enclosing_stmt(#20121,#20119)
+expr_containers(#20121,#20001)
+#20123=*
+exprs(#20123,78,#20121,0,"amount")
+hasLocation(#20123,#20079)
+enclosing_stmt(#20123,#20119)
+expr_containers(#20123,#20001)
+literals("amount","amount",#20123)
+decl(#20123,#20090)
+#20124=*
+exprs(#20124,3,#20121,1,"1_234_500")
+hasLocation(#20124,#20083)
+enclosing_stmt(#20124,#20119)
+expr_containers(#20124,#20001)
+literals("1234500","1_234_500",#20124)
+#20125=*
+entry_cfg_node(#20125,#20001)
+#20126=@"loc,{#10000},1,1,1,0"
+locations_default(#20126,#10000,1,1,1,0)
+hasLocation(#20125,#20126)
+#20127=*
+exit_cfg_node(#20127,#20001)
+hasLocation(#20127,#20087)
+successor(#20119,#20123)
+successor(#20124,#20121)
+successor(#20123,#20124)
+successor(#20121,#20127)
+successor(#20113,#20117)
+successor(#20118,#20115)
+successor(#20117,#20118)
+successor(#20115,#20119)
+successor(#20107,#20111)
+successor(#20112,#20109)
+successor(#20111,#20112)
+successor(#20109,#20113)
+successor(#20101,#20105)
+successor(#20106,#20103)
+successor(#20105,#20106)
+successor(#20103,#20107)
+successor(#20095,#20099)
+successor(#20100,#20097)
+successor(#20099,#20100)
+successor(#20097,#20101)
+successor(#20093,#20094)
+successor(#20094,#20095)
+successor(#20091,#20092)
+successor(#20092,#20093)
+successor(#20125,#20091)
+numlines(#10000,8,7,7)
+filetype(#10000,"javascript")
From 4bc91c4439a36f09fb6ea249e3d9703c76c6bdc6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Thu, 17 Sep 2020 14:10:07 +0200
Subject: [PATCH 042/185] add support for Promise.any
---
javascript/ql/src/semmle/javascript/Promises.qll | 4 ++--
javascript/ql/test/library-tests/Promises/flow2.js | 6 ++++++
.../ql/test/library-tests/Promises/tests.expected | 14 ++++++++++++++
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll
index a8231d75dce..4f730b36f0a 100644
--- a/javascript/ql/src/semmle/javascript/Promises.qll
+++ b/javascript/ql/src/semmle/javascript/Promises.qll
@@ -117,11 +117,11 @@ class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
}
/**
- * An aggregated promise produced either by `Promise.all` or `Promise.race`.
+ * An aggregated promise produced either by `Promise.all`, `Promise.race`, or `Promise.any`.
*/
class AggregateES2015PromiseDefinition extends PromiseCreationCall {
AggregateES2015PromiseDefinition() {
- exists(string m | m = "all" or m = "race" |
+ exists(string m | m = "all" or m = "race" or m = "any" |
this = DataFlow::globalVarRef("Promise").getAMemberCall(m)
)
}
diff --git a/javascript/ql/test/library-tests/Promises/flow2.js b/javascript/ql/test/library-tests/Promises/flow2.js
index ef91740e0d5..ccafb83fd3f 100644
--- a/javascript/ql/test/library-tests/Promises/flow2.js
+++ b/javascript/ql/test/library-tests/Promises/flow2.js
@@ -18,4 +18,10 @@
var [clean3, tainted3] = await Promise.all(["clean", Promise.resolve(source)]);
sink(clean3); // OK
sink(tainted3); // NOT OK - but only flagged by taint-tracking
+
+ var tainted4 = await Promise.race(["clean", Promise.resolve(source)]);
+ sink(tainted4); // NOT OK - but only flagged by taint-tracking
+
+ var tainted5 = await Promise.any(["clean", Promise.resolve(source)]);
+ sink(tainted5); // NOT OK - but only flagged by taint-tracking
});
\ No newline at end of file
diff --git a/javascript/ql/test/library-tests/Promises/tests.expected b/javascript/ql/test/library-tests/Promises/tests.expected
index 21c685e0c40..00320cc83fc 100644
--- a/javascript/ql/test/library-tests/Promises/tests.expected
+++ b/javascript/ql/test/library-tests/Promises/tests.expected
@@ -9,6 +9,12 @@ test_ResolvedPromiseDefinition
| flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:46:18:52 | "clean" |
| flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:55:18:77 | Promise ... source) |
| flow2.js:18:55:18:77 | Promise ... source) | flow2.js:18:71:18:76 | source |
+| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:37:22:43 | "clean" |
+| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) |
+| flow2.js:22:46:22:68 | Promise ... source) | flow2.js:22:62:22:67 | source |
+| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:36:25:42 | "clean" |
+| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) |
+| flow2.js:25:45:25:67 | Promise ... source) | flow2.js:25:61:25:66 | source |
| flow.js:4:11:4:33 | Promise ... source) | flow.js:4:27:4:32 | source |
| flow.js:20:2:20:24 | Promise ... source) | flow.js:20:18:20:23 | source |
| flow.js:22:2:22:24 | Promise ... source) | flow.js:22:18:22:23 | source |
@@ -201,6 +207,8 @@ flow
| flow2.js:2:15:2:22 | "source" | flow2.js:6:8:6:13 | arr[0] |
| flow2.js:2:15:2:22 | "source" | flow2.js:12:7:12:13 | tainted |
| flow2.js:2:15:2:22 | "source" | flow2.js:16:7:16:14 | tainted2 |
+| flow2.js:2:15:2:22 | "source" | flow2.js:23:7:23:14 | tainted4 |
+| flow2.js:2:15:2:22 | "source" | flow2.js:26:7:26:14 | tainted5 |
| flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 |
| flow.js:2:15:2:22 | "source" | flow.js:8:7:8:14 | await p2 |
| flow.js:2:15:2:22 | "source" | flow.js:17:8:17:8 | e |
@@ -255,6 +263,12 @@ typetrack
| flow2.js:18:27:18:79 | await P ... urce)]) | flow2.js:18:33:18:79 | Promise ... urce)]) | load $PromiseResolveField$ |
| flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:45:18:78 | ["clean ... ource)] | copy $PromiseResolveField$ |
| flow2.js:18:33:18:79 | Promise ... urce)]) | flow2.js:18:45:18:78 | ["clean ... ource)] | store $PromiseResolveField$ |
+| flow2.js:22:17:22:70 | await P ... urce)]) | flow2.js:22:23:22:70 | Promise ... urce)]) | load $PromiseResolveField$ |
+| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) | copy $PromiseResolveField$ |
+| flow2.js:22:23:22:70 | Promise ... urce)]) | flow2.js:22:46:22:68 | Promise ... source) | store $PromiseResolveField$ |
+| flow2.js:25:17:25:69 | await P ... urce)]) | flow2.js:25:23:25:69 | Promise ... urce)]) | load $PromiseResolveField$ |
+| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) | copy $PromiseResolveField$ |
+| flow2.js:25:23:25:69 | Promise ... urce)]) | flow2.js:25:45:25:67 | Promise ... source) | store $PromiseResolveField$ |
| flow.js:20:2:20:43 | Promise ... ink(x)) | flow.js:20:36:20:42 | sink(x) | copy $PromiseResolveField$ |
| flow.js:20:2:20:43 | Promise ... ink(x)) | flow.js:20:36:20:42 | sink(x) | store $PromiseResolveField$ |
| flow.js:20:31:20:31 | x | flow.js:20:2:20:24 | Promise ... source) | load $PromiseResolveField$ |
From 4571ba38a5f1249be8e2a766bc567bc5e655e644 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 21 Sep 2020 10:51:36 +0200
Subject: [PATCH 043/185] add change-note for es2021
---
change-notes/1.26/analysis-javascript.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md
index 392e4bebeb7..81ceb96b0ac 100644
--- a/change-notes/1.26/analysis-javascript.md
+++ b/change-notes/1.26/analysis-javascript.md
@@ -18,6 +18,7 @@
- [underscore](https://www.npmjs.com/package/underscore)
* Analyzing files with the ".cjs" extension is now supported.
+* ES2021 features are now supported.
## New queries
From 441fbe32154b93702eaba6c8ad23658aaa0812fc Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 3 Sep 2020 15:17:06 +0200
Subject: [PATCH 044/185] Add Java test file for sign analysis
---
.../dataflow/sign-analysis/A.java | 12 ++++++++++++
.../sign-analysis/SignAnalysis.expected | 3 +++
.../dataflow/sign-analysis/SignAnalysis.ql | 19 +++++++++++++++++++
3 files changed, 34 insertions(+)
create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/A.java
create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
create mode 100644 java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/A.java b/java/ql/test/library-tests/dataflow/sign-analysis/A.java
new file mode 100644
index 00000000000..158f4d5a18d
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/sign-analysis/A.java
@@ -0,0 +1,12 @@
+public class A {
+ int f1(int x, int y) {
+ if (x < 0) {
+ return x; // strictly negative
+ }
+ if (x < y) {
+ return y; // y is strictly positive because of the bound on x above
+ }
+
+ return 0;
+ }
+}
diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
new file mode 100644
index 00000000000..1e3fe9acf27
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
@@ -0,0 +1,3 @@
+| A.java:4:14:4:14 | x | negative strictlyNegative |
+| A.java:6:9:6:9 | x | positive |
+| A.java:7:14:7:14 | y | positive strictlyPositive |
diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
new file mode 100644
index 00000000000..7dd564dd77d
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
@@ -0,0 +1,19 @@
+import java
+import semmle.code.java.dataflow.SignAnalysis
+
+string getASignString(Expr i) {
+ positive(i) and
+ result = "positive"
+ or
+ negative(i) and
+ result = "negative"
+ or
+ strictlyPositive(i) and
+ result = "strictlyPositive"
+ or
+ strictlyNegative(i) and
+ result = "strictlyNegative"
+}
+
+from Expr e
+select e, strictconcat(string s | s = getASignString(e) | s, " ")
From 8bf4a4209c9026c3b87a59a24df26c8c093c4552 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 2 Sep 2020 16:52:17 +0200
Subject: [PATCH 045/185] C#: Sign analysis
Synced between Java and C# through `identical-files.json`.
---
config/identical-files.json | 20 +-
csharp/ql/src/Linq/Helpers.qll | 11 +
.../code/csharp/commons/ComparisonTest.qll | 2 +-
.../semmle/code/csharp/controlflow/Guards.qll | 27 +
.../semmle/code/csharp/dataflow/Nullness.qll | 2 -
.../src/semmle/code/csharp/dataflow/SSA.qll | 7 +
.../code/csharp/dataflow/SignAnalysis.qll | 9 +
.../internal/rangeanalysis/ConstantUtils.qll | 64 ++
.../dataflow/internal/rangeanalysis/Sign.qll | 219 +++++++
.../rangeanalysis/SignAnalysisCommon.qll | 293 +++++++++
.../rangeanalysis/SignAnalysisSpecific.qll | 283 +++++++++
.../rangeanalysis/SsaReadPositionCommon.qll | 57 ++
.../rangeanalysis/SsaReadPositionSpecific.qll | 16 +
.../internal/rangeanalysis/SsaUtils.qll | 42 ++
.../code/csharp/exprs/ComparisonOperation.qll | 3 +
.../signanalysis/MissingSign.expected | 4 +
.../dataflow/signanalysis/MissingSign.ql | 15 +
.../dataflow/signanalysis/SignAnalysis.cs | 455 ++++++++++++++
.../signanalysis/SignAnalysis.expected | 217 +++++++
.../dataflow/signanalysis/SignAnalysis.ql | 21 +
.../code/java/dataflow/ModulusAnalysis.qll | 1 +
.../code/java/dataflow/RangeAnalysis.qll | 1 +
.../semmle/code/java/dataflow/RangeUtils.qll | 58 +-
.../code/java/dataflow/SignAnalysis.qll | 594 +-----------------
.../dataflow/internal/rangeanalysis/Sign.qll | 219 +++++++
.../rangeanalysis/SignAnalysisCommon.qll | 293 +++++++++
.../rangeanalysis/SignAnalysisSpecific.qll | 235 +++++++
.../rangeanalysis/SsaReadPositionCommon.qll | 57 ++
.../rangeanalysis/SsaReadPositionSpecific.qll | 15 +
.../sign-analysis/SignAnalysis.expected | 4 +-
.../dataflow/sign-analysis/SignAnalysis.ql | 12 +-
31 files changed, 2592 insertions(+), 664 deletions(-)
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
create mode 100644 csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected
create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql
create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
create mode 100644 csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
diff --git a/config/identical-files.json b/config/identical-files.json
index 7e62497c9bf..e0a4e8dabf4 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -50,6 +50,18 @@
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
"python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll"
],
+ "SsaReadPosition Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
+ ],
+ "Sign Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
+ ],
+ "SignAnalysis Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
+ ],
"C++ SubBasicBlocks": [
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
@@ -87,7 +99,7 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
- "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
],
"IR IRType": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
@@ -109,11 +121,11 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
],
- "IR TInstruction":[
+ "IR TInstruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
],
- "IR TIRVariable":[
+ "IR TIRVariable": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
],
@@ -381,4 +393,4 @@
"javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
"python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
]
-}
+}
\ No newline at end of file
diff --git a/csharp/ql/src/Linq/Helpers.qll b/csharp/ql/src/Linq/Helpers.qll
index 41d1e305ca6..e1b1beb1222 100644
--- a/csharp/ql/src/Linq/Helpers.qll
+++ b/csharp/ql/src/Linq/Helpers.qll
@@ -132,6 +132,17 @@ class AnyCall extends MethodCall {
}
}
+/** A LINQ Count(...) call. */
+class CountCall extends MethodCall {
+ CountCall() {
+ exists(Method m |
+ m = getTarget() and
+ isEnumerableType(m.getDeclaringType()) and
+ m.hasName("Count")
+ )
+ }
+}
+
/** A variable of type IEnumerable<T>, for some T. */
class IEnumerableSequence extends Variable {
IEnumerableSequence() { isIEnumerableType(getType()) }
diff --git a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll
index 5d083679f9d..ea7d250abb0 100644
--- a/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll
+++ b/csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll
@@ -2,7 +2,7 @@
* Provides classes for capturing various ways of performing comparison tests.
*/
-import csharp
+private import csharp
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
index 89042f7de07..f81b3b09212 100644
--- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
+++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
@@ -31,6 +31,33 @@ class Guard extends Expr {
predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AccessOrCallExpr sub, AbstractValue v) {
isGuardedByNode(cfn, this, sub, v)
}
+
+ /**
+ * Holds if guard is an equality test between `e1` and `e2`. If the test is
+ * negated, that is `!=`, then `polarity` is false, otherwise `polarity` is
+ * true.
+ */
+ predicate isEquality(Expr e1, Expr e2, boolean polarity) {
+ exists(Expr exp1, Expr exp2 | equalityGuard(exp1, exp2, polarity) |
+ e1 = exp1 and e2 = exp2
+ or
+ e2 = exp1 and e1 = exp2
+ )
+ }
+
+ private predicate equalityGuard(Expr e1, Expr e2, boolean polarity) {
+ exists(ComparisonTest eqtest |
+ eqtest.getExpr() = this and
+ eqtest.getAnArgument() = e1 and
+ eqtest.getAnArgument() = e2 and
+ not e1 = e2 and
+ (
+ polarity = true and eqtest.getComparisonKind().isEquality()
+ or
+ polarity = false and eqtest.getComparisonKind().isInequality()
+ )
+ )
+ }
}
/** An abstract value. */
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll
index a537d08aabc..ed10e700966 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll
@@ -21,10 +21,8 @@ import csharp
private import ControlFlow
private import internal.CallableReturns
private import semmle.code.csharp.commons.Assertions
-private import semmle.code.csharp.commons.ComparisonTest
private import semmle.code.csharp.controlflow.Guards as G
private import semmle.code.csharp.controlflow.Guards::AbstractValues
-private import semmle.code.csharp.dataflow.SSA
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.Test
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
index adcfdfaaca7..50a69857d0c 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
@@ -2537,6 +2537,13 @@ module Ssa {
)
}
+ /** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
+ predicate hasInputFromBlock(Definition inp, BasicBlock bb) {
+ this.getAnInput() = inp and
+ this.getBasicBlock().getAPredecessor() = bb and
+ inp.isLiveAtEndOfBlock(bb)
+ }
+
override string toString() {
result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")"
}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll
new file mode 100644
index 00000000000..3742f87de25
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/SignAnalysis.qll
@@ -0,0 +1,9 @@
+/**
+ * Provides sign analysis to determine whether expression are always positive
+ * or negative.
+ *
+ * The analysis is implemented as an abstract interpretation over the
+ * three-valued domain `{negative, zero, positive}`.
+ */
+
+import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
new file mode 100644
index 00000000000..e283909155c
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
@@ -0,0 +1,64 @@
+/**
+ * Provides classes and predicates to represent constant integer expressions.
+ */
+
+private import csharp
+private import Ssa
+
+/**
+ * Holds if property `p` matches `property` in `baseClass` or any overrides.
+ */
+predicate propertyOverrides(Property p, string baseClass, string property) {
+ exists(Property p2 |
+ p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
+ p2.hasName(property)
+ |
+ p.overridesOrImplementsOrEquals(p2)
+ )
+}
+
+/**
+ * Holds if expression `e` is either
+ * - a compile time constant with integer value `val`, or
+ * - a read of a compile time constant with integer value `val`, or
+ * - a read of the `Length` of an array with `val` lengths.
+ */
+private predicate constantIntegerExpr(Expr e, int val) {
+ e.getValue().toInt() = val
+ or
+ exists(ExplicitDefinition v, Expr src |
+ e = v.getARead() and
+ src = v.getADefinition().getSource() and
+ constantIntegerExpr(src, val)
+ )
+ or
+ isArrayLengthAccess(e, val)
+}
+
+private int getArrayLength(ArrayCreation arrCreation, int index) {
+ constantIntegerExpr(arrCreation.getLengthArgument(index), result)
+}
+
+private int getArrayLengthRec(ArrayCreation arrCreation, int index) {
+ index = 0 and result = getArrayLength(arrCreation, 0)
+ or
+ index > 0 and
+ result = getArrayLength(arrCreation, index) * getArrayLengthRec(arrCreation, index - 1)
+}
+
+private predicate isArrayLengthAccess(PropertyAccess pa, int length) {
+ propertyOverrides(pa.getTarget(), "System.Array", "Length") and
+ exists(ExplicitDefinition arr, ArrayCreation arrCreation |
+ getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and
+ arrCreation = arr.getADefinition().getSource() and
+ pa.getQualifier() = arr.getARead()
+ )
+}
+
+/** An expression that always has the same integer value. */
+class ConstantIntegerExpr extends Expr {
+ ConstantIntegerExpr() { constantIntegerExpr(this, _) }
+
+ /** Gets the integer value of this expression. */
+ int getIntValue() { constantIntegerExpr(this, result) }
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll
new file mode 100644
index 00000000000..10ca946a044
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll
@@ -0,0 +1,219 @@
+newtype TSign =
+ TNeg() or
+ TZero() or
+ TPos()
+
+/** Class representing expression signs (+, -, 0). */
+class Sign extends TSign {
+ /** Gets the string representation of this sign. */
+ string toString() {
+ result = "-" and this = TNeg()
+ or
+ result = "0" and this = TZero()
+ or
+ result = "+" and this = TPos()
+ }
+
+ /** Gets a possible sign after incrementing an expression that has this sign. */
+ Sign inc() {
+ this = TNeg() and result = TNeg()
+ or
+ this = TNeg() and result = TZero()
+ or
+ this = TZero() and result = TPos()
+ or
+ this = TPos() and result = TPos()
+ }
+
+ /** Gets a possible sign after decrementing an expression that has this sign. */
+ Sign dec() { result.inc() = this }
+
+ /** Gets a possible sign after negating an expression that has this sign. */
+ Sign neg() {
+ this = TNeg() and result = TPos()
+ or
+ this = TZero() and result = TZero()
+ or
+ this = TPos() and result = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after bitwise complementing an expression that has this
+ * sign.
+ */
+ Sign bitnot() {
+ this = TNeg() and result = TPos()
+ or
+ this = TNeg() and result = TZero()
+ or
+ this = TZero() and result = TNeg()
+ or
+ this = TPos() and result = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after adding an expression with sign `s` to an expression
+ * that has this sign.
+ */
+ Sign add(Sign s) {
+ this = TZero() and result = s
+ or
+ s = TZero() and result = this
+ or
+ this = s and this = result
+ or
+ this = TPos() and s = TNeg()
+ or
+ this = TNeg() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after multiplying an expression with sign `s` to an expression
+ * that has this sign.
+ */
+ Sign mul(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = TZero() and s = TZero()
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after integer dividing an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign div(Sign s) {
+ result = TZero() and s = TNeg() // ex: 3 / -5 = 0
+ or
+ result = TZero() and s = TPos() // ex: 3 / 5 = 0
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after modulo dividing an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign rem(Sign s) {
+ result = TZero() and s = TNeg()
+ or
+ result = TZero() and s = TPos()
+ or
+ result = this and s = TNeg()
+ or
+ result = this and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `and` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitand(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = TZero() and s = TZero()
+ or
+ result = TZero() and this = TPos()
+ or
+ result = TZero() and s = TPos()
+ or
+ result = TNeg() and this = TNeg() and s = TNeg()
+ or
+ result = TPos() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TNeg()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `or` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitor(Sign s) {
+ result = TZero() and this = TZero() and s = TZero()
+ or
+ result = TNeg() and this = TNeg()
+ or
+ result = TNeg() and s = TNeg()
+ or
+ result = TPos() and this = TPos() and s = TZero()
+ or
+ result = TPos() and this = TZero() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `xor` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitxor(Sign s) {
+ result = TZero() and this = s
+ or
+ result = this and s = TZero()
+ or
+ result = s and this = TZero()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after left shift of an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign lshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ this != TZero() and s != TZero()
+ }
+
+ /**
+ * Gets a possible sign after right shift of an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign rshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ result = TNeg() and this = TNeg()
+ or
+ result != TNeg() and this = TPos() and s != TZero()
+ }
+
+ /**
+ * Gets a possible sign after unsigned right shift of an expression that has
+ * this sign by an expression with sign `s`.
+ */
+ Sign urshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ result != TZero() and this = TNeg() and s != TZero()
+ or
+ result != TNeg() and this = TPos() and s != TZero()
+ }
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
new file mode 100644
index 00000000000..66b38433eef
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
@@ -0,0 +1,293 @@
+/**
+ * Provides sign analysis to determine whether expression are always positive
+ * or negative.
+ *
+ * The analysis is implemented as an abstract interpretation over the
+ * three-valued domain `{negative, zero, positive}`.
+ */
+
+private import SignAnalysisSpecific::Private
+private import SsaReadPositionCommon
+private import Sign
+
+/** Gets the sign of `e` if this can be directly determined. */
+Sign certainExprSign(Expr e) {
+ exists(int i | e.(ConstantIntegerExpr).getIntValue() = i |
+ i < 0 and result = TNeg()
+ or
+ i = 0 and result = TZero()
+ or
+ i > 0 and result = TPos()
+ )
+ or
+ not exists(e.(ConstantIntegerExpr).getIntValue()) and
+ (
+ exists(float f | f = getNonIntegerValue(e) |
+ f < 0 and result = TNeg()
+ or
+ f = 0 and result = TZero()
+ or
+ f > 0 and result = TPos()
+ )
+ or
+ exists(string charlit | charlit = getCharValue(e) |
+ if charlit.regexpMatch("\\u0000") then result = TZero() else result = TPos()
+ )
+ or
+ containerSizeAccess(e) and
+ (result = TPos() or result = TZero())
+ or
+ positiveExpression(e) and result = TPos()
+ )
+}
+
+/** Holds if the sign of `e` is too complicated to determine. */
+predicate unknownSign(Expr e) {
+ not exists(certainExprSign(e)) and
+ (
+ exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt()))
+ or
+ exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
+ or
+ exists(CastExpr cast, Type fromtyp |
+ cast = e and
+ fromtyp = cast.getExpr().getType() and
+ not fromtyp instanceof NumericOrCharType
+ )
+ or
+ unknownIntegerAccess(e)
+ )
+}
+
+/**
+ * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
+ * to only include bounds for which we might determine a sign.
+ */
+private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
+ exists(boolean testIsTrue, ComparisonExpr comp |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and
+ not unknownSign(lowerbound)
+ |
+ testIsTrue = true and
+ comp.getLesserOperand() = lowerbound and
+ comp.getGreaterOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = true else isStrict = false)
+ or
+ testIsTrue = false and
+ comp.getGreaterOperand() = lowerbound and
+ comp.getLesserOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = false else isStrict = true)
+ )
+}
+
+/**
+ * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
+ * to only include bounds for which we might determine a sign.
+ */
+private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
+ exists(boolean testIsTrue, ComparisonExpr comp |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and
+ not unknownSign(upperbound)
+ |
+ testIsTrue = true and
+ comp.getGreaterOperand() = upperbound and
+ comp.getLesserOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = true else isStrict = false)
+ or
+ testIsTrue = false and
+ comp.getLesserOperand() = upperbound and
+ comp.getGreaterOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = false else isStrict = true)
+ )
+}
+
+/**
+ * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
+ * restricted to only include bounds for which we might determine a sign. The
+ * boolean `isEq` gives the polarity:
+ * - `isEq = true` : `v = eqbound`
+ * - `isEq = false` : `v != eqbound`
+ */
+private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) {
+ exists(Guard guard, boolean testIsTrue, boolean polarity |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(guard, pos, testIsTrue) and
+ guard.isEquality(eqbound, ssaRead(v, 0), polarity) and
+ isEq = polarity.booleanXor(testIsTrue).booleanNot() and
+ not unknownSign(eqbound)
+ )
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
+ * order for `v` to be positive.
+ */
+private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ upperBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, true)
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
+ * order for `v` to be negative.
+ */
+private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, true)
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
+ * can be zero.
+ */
+private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) or
+ upperBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, _)
+}
+
+/** Holds if `bound` allows `v` to be positive at `pos`. */
+private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ posBound(bound, v, pos) and TPos() = exprSign(bound)
+}
+
+/** Holds if `bound` allows `v` to be negative at `pos`. */
+private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ negBound(bound, v, pos) and TNeg() = exprSign(bound)
+}
+
+/** Holds if `bound` allows `v` to be zero at `pos`. */
+private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound)
+ or
+ lowerBound(bound, v, pos, false) and TZero() = exprSign(bound)
+ or
+ upperBound(bound, v, pos, _) and TPos() = exprSign(bound)
+ or
+ upperBound(bound, v, pos, false) and TZero() = exprSign(bound)
+ or
+ eqBound(bound, v, pos, true) and TZero() = exprSign(bound)
+ or
+ eqBound(bound, v, pos, false) and TZero() != exprSign(bound)
+}
+
+/**
+ * Holds if there is a bound that might restrict whether `v` has the sign `s`
+ * at `pos`.
+ */
+private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) {
+ s = TPos() and posBound(_, v, pos)
+ or
+ s = TNeg() and negBound(_, v, pos)
+ or
+ s = TZero() and zeroBound(_, v, pos)
+}
+
+pragma[noinline]
+private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) {
+ // SSA variable can have sign `result`
+ result = ssaDefSign(v) and
+ pos.hasReadOfVar(v) and
+ // there are guards at this position on `v` that might restrict it to be sign `result`.
+ // (So we need to check if they are satisfied)
+ hasGuard(v, pos, result)
+}
+
+pragma[noinline]
+private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) {
+ // SSA variable can have sign `result`
+ result = ssaDefSign(v) and
+ pos.hasReadOfVar(v) and
+ // there's no guard at this position on `v` that might restrict it to be sign `result`.
+ not hasGuard(v, pos, result)
+}
+
+/**
+ * Gets the sign of `v` at read position `pos`, when there's at least one guard
+ * on `v` at position `pos`. Each bound corresponding to a given sign must be met
+ * in order for `v` to be of that sign.
+ */
+private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
+ result = TPos() and
+ forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
+ or
+ result = TNeg() and
+ forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
+ or
+ result = TZero() and
+ forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
+}
+
+/** Gets a possible sign for `v` at `pos`. */
+Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
+ result = unguardedSsaSign(v, pos)
+ or
+ result = guardedSsaSign(v, pos) and
+ result = guardedSsaSignOk(v, pos)
+}
+
+/** Gets a possible sign for `v`. */
+pragma[nomagic]
+Sign ssaDefSign(SsaVariable v) {
+ result = explicitSsaDefSign(v)
+ or
+ result = implicitSsaDefSign(v)
+ or
+ exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge |
+ v = phi and
+ edge.phiInput(phi, inp) and
+ result = ssaSign(inp, edge)
+ )
+}
+
+/** Gets a possible sign for `e`. */
+cached
+Sign exprSign(Expr e) {
+ result = certainExprSign(e)
+ or
+ not exists(certainExprSign(e)) and
+ (
+ unknownSign(e)
+ or
+ exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e))
+ or
+ e =
+ any(VarAccess access |
+ not exists(SsaVariable v | getARead(v) = access) and
+ (
+ result = fieldSign(getField(access.(FieldAccess))) or
+ not access instanceof FieldAccess
+ )
+ )
+ or
+ result = specificSubExprSign(e)
+ )
+}
+
+/** Holds if `e` can be positive and cannot be negative. */
+predicate positive(Expr e) {
+ exprSign(e) = TPos() and
+ not exprSign(e) = TNeg()
+}
+
+/** Holds if `e` can be negative and cannot be positive. */
+predicate negative(Expr e) {
+ exprSign(e) = TNeg() and
+ not exprSign(e) = TPos()
+}
+
+/** Holds if `e` is strictly positive. */
+predicate strictlyPositive(Expr e) {
+ exprSign(e) = TPos() and
+ not exprSign(e) = TNeg() and
+ not exprSign(e) = TZero()
+}
+
+/** Holds if `e` is strictly negative. */
+predicate strictlyNegative(Expr e) {
+ exprSign(e) = TNeg() and
+ not exprSign(e) = TPos() and
+ not exprSign(e) = TZero()
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
new file mode 100644
index 00000000000..75adec96909
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
@@ -0,0 +1,283 @@
+/**
+ * Provides C#-specific definitions for use in sign analysis.
+ */
+module Private {
+ private import SsaUtils as SU
+ private import csharp as CS
+ private import ConstantUtils as CU
+ private import semmle.code.csharp.controlflow.Guards as G
+ import Impl
+
+ class Guard = G::Guard;
+
+ class ConstantIntegerExpr = CU::ConstantIntegerExpr;
+
+ class SsaVariable = CS::Ssa::Definition;
+
+ class SsaPhiNode = CS::Ssa::PhiNode;
+
+ class VarAccess = CS::AssignableAccess;
+
+ class FieldAccess = CS::FieldAccess;
+
+ class CharacterLiteral = CS::CharLiteral;
+
+ class IntegerLiteral = CS::IntegerLiteral;
+
+ class LongLiteral = CS::LongLiteral;
+
+ class CastExpr = CS::CastExpr;
+
+ class Type = CS::Type;
+
+ class Expr = CS::Expr;
+
+ predicate ssaRead = SU::ssaRead/2;
+}
+
+private module Impl {
+ private import csharp
+ private import SsaUtils
+ private import ConstantUtils
+ private import semmle.code.csharp.controlflow.Guards
+ private import Linq.Helpers
+ private import Sign
+ private import SignAnalysisCommon
+ private import SsaReadPositionCommon
+ private import semmle.code.csharp.commons.ComparisonTest
+
+ private class BooleanValue = AbstractValues::BooleanValue;
+
+ float getNonIntegerValue(Expr e) {
+ exists(string s |
+ s = e.getValue() and
+ result = s.toFloat() and
+ not exists(s.toInt())
+ )
+ }
+
+ string getCharValue(Expr e) { result = e.getValue() and e.getType() instanceof CharType }
+
+ predicate containerSizeAccess(Expr e) {
+ exists(Property p | p = e.(PropertyAccess).getTarget() |
+ propertyOverrides(p, "System.Collections.Generic.IEnumerable<>", "Count") or
+ propertyOverrides(p, "System.Collections.ICollection", "Count") or
+ propertyOverrides(p, "System.String", "Length") or
+ propertyOverrides(p, "System.Array", "Length")
+ )
+ or
+ e instanceof CountCall
+ }
+
+ predicate positiveExpression(Expr e) { e instanceof SizeofExpr }
+
+ class NumericOrCharType extends Type {
+ NumericOrCharType() {
+ this instanceof CharType or
+ this instanceof IntegralType or
+ this instanceof FloatingPointType or
+ this instanceof DecimalType or
+ this instanceof Enum or
+ this instanceof PointerType // should be similar to unsigned integers
+ }
+ }
+
+ Sign explicitSsaDefSign(Ssa::ExplicitDefinition v) {
+ exists(AssignableDefinition def | def = v.getADefinition() |
+ result = exprSign(def.getSource())
+ or
+ not exists(def.getSource()) and
+ not def.getElement() instanceof MutatorOperation
+ or
+ result = exprSign(def.getElement().(IncrementOperation).getOperand()).inc()
+ or
+ result = exprSign(def.getElement().(DecrementOperation).getOperand()).dec()
+ )
+ }
+
+ Sign implicitSsaDefSign(Ssa::ImplicitDefinition v) {
+ result = fieldSign(v.getSourceVariable().getAssignable()) or
+ not v.getSourceVariable().getAssignable() instanceof Field
+ }
+
+ pragma[inline]
+ Sign ssaVariableSign(Ssa::Definition v, Expr e) {
+ result = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e))
+ }
+
+ /** Gets a possible sign for `f`. */
+ Sign fieldSign(Field f) {
+ if f.fromSource() and f.isEffectivelyPrivate()
+ then
+ result = exprSign(f.getAnAssignedValue())
+ or
+ any(IncrementOperation inc).getOperand() = f.getAnAccess() and result = fieldSign(f).inc()
+ or
+ any(DecrementOperation dec).getOperand() = f.getAnAccess() and result = fieldSign(f).dec()
+ or
+ exists(AssignOperation a | a.getLValue() = f.getAnAccess() | result = exprSign(a))
+ or
+ not exists(f.getInitializer()) and result = TZero()
+ else any()
+ }
+
+ predicate unknownIntegerAccess(Expr e) {
+ e.getType() instanceof NumericOrCharType and
+ not e = getARead(_) and
+ not e instanceof FieldAccess and
+ // The expression types that are listed here are the ones handled in `specificSubExprSign`.
+ // Keep them in sync.
+ not e instanceof AssignExpr and
+ not e instanceof AssignOperation and
+ not e instanceof UnaryPlusExpr and
+ not e instanceof PostIncrExpr and
+ not e instanceof PostDecrExpr and
+ not e instanceof PreIncrExpr and
+ not e instanceof PreDecrExpr and
+ not e instanceof UnaryMinusExpr and
+ not e instanceof ComplementExpr and
+ not e instanceof AddExpr and
+ not e instanceof SubExpr and
+ not e instanceof MulExpr and
+ not e instanceof DivExpr and
+ not e instanceof RemExpr and
+ not e instanceof BitwiseAndExpr and
+ not e instanceof BitwiseOrExpr and
+ not e instanceof BitwiseXorExpr and
+ not e instanceof LShiftExpr and
+ not e instanceof RShiftExpr and
+ not e instanceof ConditionalExpr and
+ not e instanceof RefExpr and
+ not e instanceof LocalVariableDeclAndInitExpr and
+ not e instanceof SwitchCaseExpr and
+ not e instanceof CastExpr and
+ not e instanceof SwitchExpr and
+ not e instanceof NullCoalescingExpr
+ }
+
+ Sign specificSubExprSign(Expr e) {
+ // The expression types that are handled here should be excluded in `unknownIntegerAccess`.
+ // Keep them in sync.
+ result = exprSign(e.(AssignExpr).getRValue())
+ or
+ result = exprSign(e.(AssignOperation).getExpandedAssignment())
+ or
+ result = exprSign(e.(UnaryPlusExpr).getOperand())
+ or
+ result = exprSign(e.(PostIncrExpr).getOperand())
+ or
+ result = exprSign(e.(PostDecrExpr).getOperand())
+ or
+ result = exprSign(e.(PreIncrExpr).getOperand()).inc()
+ or
+ result = exprSign(e.(PreDecrExpr).getOperand()).dec()
+ or
+ result = exprSign(e.(UnaryMinusExpr).getOperand()).neg()
+ or
+ result = exprSign(e.(ComplementExpr).getOperand()).bitnot()
+ or
+ e =
+ any(DivExpr div |
+ result = exprSign(div.getLeftOperand()) and
+ result != TZero() and
+ div.getRightOperand().(RealLiteral).getValue().toFloat() = 0
+ )
+ or
+ exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) |
+ e instanceof AddExpr and result = s1.add(s2)
+ or
+ e instanceof SubExpr and result = s1.add(s2.neg())
+ or
+ e instanceof MulExpr and result = s1.mul(s2)
+ or
+ e instanceof DivExpr and result = s1.div(s2)
+ or
+ e instanceof RemExpr and result = s1.rem(s2)
+ or
+ e instanceof BitwiseAndExpr and result = s1.bitand(s2)
+ or
+ e instanceof BitwiseOrExpr and result = s1.bitor(s2)
+ or
+ e instanceof BitwiseXorExpr and result = s1.bitxor(s2)
+ or
+ e instanceof LShiftExpr and result = s1.lshift(s2)
+ or
+ e instanceof RShiftExpr and result = s1.rshift(s2)
+ )
+ or
+ result = exprSign(e.(ConditionalExpr).getAChild())
+ or
+ result = exprSign(e.(NullCoalescingExpr).getAChild())
+ or
+ result = exprSign(e.(SwitchExpr).getACase().getBody())
+ or
+ result = exprSign(e.(CastExpr).getExpr())
+ or
+ result = exprSign(e.(SwitchCaseExpr).getBody())
+ or
+ result = exprSign(e.(LocalVariableDeclAndInitExpr).getInitializer())
+ or
+ result = exprSign(e.(RefExpr).getExpr())
+ }
+
+ private Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) }
+
+ private Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) }
+
+ pragma[noinline]
+ private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) {
+ lhs = binaryOpLhsSign(e) and
+ rhs = binaryOpRhsSign(e)
+ }
+
+ Expr getARead(Ssa::Definition v) { result = v.getARead() }
+
+ Field getField(FieldAccess fa) { result = fa.getTarget() }
+
+ Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().getElement() }
+
+ Guard getComparisonGuard(ComparisonExpr ce) { result = ce.getExpr() }
+
+ /**
+ * Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
+ */
+ predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
+ exists(BooleanValue b | b.getValue() = testIsTrue |
+ guard.controlsNode(controlled.(SsaReadPositionBlock).getBlock().getANode(), _, b)
+ )
+ }
+
+ /** A relational comparison */
+ class ComparisonExpr extends ComparisonTest {
+ private boolean strict;
+
+ ComparisonExpr() {
+ this.getComparisonKind() =
+ any(ComparisonKind ck |
+ ck.isLessThan() and strict = true
+ or
+ ck.isLessThanEquals() and
+ strict = false
+ )
+ }
+
+ /**
+ * Gets the operand on the "greater" (or "greater-or-equal") side
+ * of this relational expression, that is, the side that is larger
+ * if the overall expression evaluates to `true`; for example on
+ * `x <= 20` this is the `20`, and on `y > 0` it is `y`.
+ */
+ Expr getGreaterOperand() { result = this.getSecondArgument() }
+
+ /**
+ * Gets the operand on the "lesser" (or "lesser-or-equal") side
+ * of this relational expression, that is, the side that is smaller
+ * if the overall expression evaluates to `true`; for example on
+ * `x <= 20` this is `x`, and on `y > 0` it is the `0`.
+ */
+ Expr getLesserOperand() { result = this.getFirstArgument() }
+
+ /** Holds if this comparison is strict, i.e. `<` or `>`. */
+ predicate isStrict() { strict = true }
+ }
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
new file mode 100644
index 00000000000..558ecd1b88b
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
@@ -0,0 +1,57 @@
+/**
+ * Provides classes for representing a position at which an SSA variable is read.
+ */
+
+private import SsaReadPositionSpecific
+
+private newtype TSsaReadPosition =
+ TSsaReadPositionBlock(BasicBlock bb) { bb = getAReadBasicBlock(_) } or
+ TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) {
+ exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock())
+ }
+
+/**
+ * A position at which an SSA variable is read. This includes both ordinary
+ * reads occurring in basic blocks and input to phi nodes occurring along an
+ * edge between two basic blocks.
+ */
+class SsaReadPosition extends TSsaReadPosition {
+ /** Holds if `v` is read at this position. */
+ abstract predicate hasReadOfVar(SsaVariable v);
+
+ /** Gets a textual representation of this SSA read position. */
+ abstract string toString();
+}
+
+/** A basic block in which an SSA variable is read. */
+class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock {
+ /** Gets the basic block corresponding to this position. */
+ BasicBlock getBlock() { this = TSsaReadPositionBlock(result) }
+
+ override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) }
+
+ override string toString() { result = "block" }
+}
+
+/**
+ * An edge between two basic blocks where the latter block has an SSA phi
+ * definition. The edge therefore has a read of an SSA variable serving as the
+ * input to the phi node.
+ */
+class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge {
+ /** Gets the source of the edge. */
+ BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) }
+
+ /** Gets the target of the edge. */
+ BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) }
+
+ override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) }
+
+ /** Holds if `inp` is an input to `phi` along this edge. */
+ predicate phiInput(SsaPhiNode phi, SsaVariable inp) {
+ phi.hasInputFromBlock(inp, getOrigBlock()) and
+ getPhiBlock() = phi.getBasicBlock()
+ }
+
+ override string toString() { result = "edge" }
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
new file mode 100644
index 00000000000..d7df9781b2a
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
@@ -0,0 +1,16 @@
+/**
+ * Provides C#-specific definitions for use in the `SsaReadPosition`.
+ */
+
+private import csharp
+
+class SsaVariable = Ssa::Definition;
+
+class SsaPhiNode = Ssa::PhiNode;
+
+class BasicBlock = Ssa::BasicBlock;
+
+/** Gets a basic block in which SSA variable `v` is read. */
+BasicBlock getAReadBasicBlock(SsaVariable v) {
+ result = v.getARead().getAControlFlowNode().getBasicBlock()
+}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
new file mode 100644
index 00000000000..7f1811c1f37
--- /dev/null
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
@@ -0,0 +1,42 @@
+/**
+ * Provides utility predicates to extend the core SSA functionality.
+ */
+
+private import csharp
+private import Ssa
+private import ConstantUtils
+
+/**
+ * Gets an expression that equals `v - delta`.
+ */
+Expr ssaRead(Definition v, int delta) {
+ result = v.getARead() and delta = 0
+ or
+ exists(AddExpr add, int d1, ConstantIntegerExpr c |
+ result = add and
+ delta = d1 - c.getIntValue()
+ |
+ add.getLeftOperand() = ssaRead(v, d1) and add.getRightOperand() = c
+ or
+ add.getRightOperand() = ssaRead(v, d1) and add.getLeftOperand() = c
+ )
+ or
+ exists(SubExpr sub, int d1, ConstantIntegerExpr c |
+ result = sub and
+ sub.getLeftOperand() = ssaRead(v, d1) and
+ sub.getRightOperand() = c and
+ delta = d1 + c.getIntValue()
+ )
+ or
+ v.(ExplicitDefinition).getADefinition().getExpr().(PreIncrExpr) = result and delta = 0
+ or
+ v.(ExplicitDefinition).getADefinition().getExpr().(PreDecrExpr) = result and delta = 0
+ or
+ v.(ExplicitDefinition).getADefinition().getExpr().(PostIncrExpr) = result and delta = 1 // x++ === ++x - 1
+ or
+ v.(ExplicitDefinition).getADefinition().getExpr().(PostDecrExpr) = result and delta = -1 // x-- === --x + 1
+ or
+ v.(ExplicitDefinition).getADefinition().getExpr().(Assignment) = result and delta = 0
+ or
+ result.(AssignExpr).getRValue() = ssaRead(v, delta)
+}
diff --git a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll
index e48aabd76a7..8b94ef5b4d7 100644
--- a/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll
+++ b/csharp/ql/src/semmle/code/csharp/exprs/ComparisonOperation.qll
@@ -57,6 +57,9 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
Expr getLesserOperand() { none() }
+
+ /** Holds if this comparison is strict, i.e. `<` or `>`. */
+ predicate isStrict() { this instanceof LTExpr or this instanceof GTExpr }
}
/**
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected
new file mode 100644
index 00000000000..72f1bdadeda
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.expected
@@ -0,0 +1,4 @@
+| SignAnalysis.cs:428:23:428:24 | access to constant A |
+| SignAnalysis.cs:448:22:448:29 | Byte* to = ... |
+| SignAnalysis.cs:450:38:450:44 | (...) ... |
+| SignAnalysis.cs:450:43:450:44 | access to local variable to |
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql
new file mode 100644
index 00000000000..b769f3eff6e
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/MissingSign.ql
@@ -0,0 +1,15 @@
+import csharp
+import semmle.code.csharp.dataflow.SignAnalysis
+
+from Expr e
+where
+ not exists(exprSign(e)) and
+ (
+ e.getType() instanceof CharType or
+ e.getType() instanceof IntegralType or
+ e.getType() instanceof FloatingPointType or
+ e.getType() instanceof DecimalType or
+ e.getType() instanceof Enum or
+ e.getType() instanceof PointerType
+ )
+select e
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
new file mode 100644
index 00000000000..13a94a18aae
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
@@ -0,0 +1,455 @@
+using System;
+using System.Linq;
+using System.Diagnostics;
+
+class SignAnalysis
+{
+ static int GetRandomValue() { return (new System.Random()).Next(0, 1000) - 500; }
+
+ int RandomValue { get => GetRandomValue(); }
+
+ int random = GetRandomValue();
+
+ int SsaSources(int p, int[] values)
+ {
+ var v = GetRandomValue();
+ if (v < 0)
+ {
+ return v;
+ }
+
+ v = RandomValue;
+ if (v < 0)
+ {
+ return v;
+ }
+
+ v = p;
+ if (v < 0)
+ {
+ return v;
+ }
+
+ v = random;
+ if (v < 0)
+ {
+ return v;
+ }
+
+ v = values[0];
+ if (v < 0)
+ {
+ return v;
+ }
+
+ int x = values[1];
+ v = x;
+ if (v < 0)
+ {
+ return v;
+ }
+
+ return 0;
+ }
+
+ void Operations(int i, int j, bool b)
+ {
+ if (i < 0 && j < 0)
+ {
+ var x = i + j;
+ System.Console.WriteLine(x); // strictly neg
+ x = i * j;
+ System.Console.WriteLine(x); // strictly pos
+ x = i / j;
+ System.Console.WriteLine(x); // pos
+ x = i - j;
+ System.Console.WriteLine(x); // no clue
+ x = i % j;
+ System.Console.WriteLine(x); // neg
+ x = i++;
+ System.Console.WriteLine(x); // strictly neg
+ x = i--;
+ System.Console.WriteLine(x); // neg
+ x = -i;
+ System.Console.WriteLine(x); // strictly pos
+ x = +i;
+ System.Console.WriteLine(x); // strictly neg
+ var l = (long)i;
+ System.Console.WriteLine(l); // strictly neg
+
+ x = i;
+ x += i;
+ System.Console.WriteLine(x); // strictly neg
+ }
+
+ if (i < 0 && j > 0)
+ {
+ var x = i + j;
+ System.Console.WriteLine(x);
+ x = i * j;
+ System.Console.WriteLine(x); // strictly neg
+ x = i / j;
+ System.Console.WriteLine(x); // neg
+ x = i - j;
+ System.Console.WriteLine(x); // strictly neg
+ x = i % j;
+ System.Console.WriteLine(x); // neg
+ x = b ? i : j;
+ System.Console.WriteLine(x); // any (except 0)
+ }
+ }
+
+ void NumericalTypes()
+ {
+ var f = 4.2f;
+ System.Console.WriteLine(f);
+ var d = 4.2;
+ System.Console.WriteLine(d);
+ var de = 4.2m;
+ System.Console.WriteLine(de);
+ var c = 'a';
+ System.Console.WriteLine(c);
+ }
+
+ int f0;
+
+ int f1;
+
+ void Field0()
+ {
+ f0++;
+ System.Console.WriteLine(f0); // strictly positive
+ f0 = 0;
+ }
+
+ void Field1()
+ {
+ f1++;
+ System.Console.WriteLine(f1); // no clue
+ f1 = -10;
+ }
+
+ void Field2()
+ {
+ System.Console.WriteLine(f1); // no clue
+ }
+
+ void Ctor()
+ {
+ var i = new Int32(); // const 0 value
+ i++;
+ System.Console.WriteLine(i); // strictly pos
+ }
+
+ int Guards(int x, int y)
+ {
+ if (x < 0)
+ {
+ return x; // strictly negative
+ }
+
+ if (y == 1)
+ {
+ return y; // strictly positive
+ }
+
+ if (y is -1)
+ {
+ return y; // strictly negative [MISSING]
+ }
+
+ if (x < y)
+ {
+ return y; // strictly positive
+ }
+
+ var b = y == 1;
+ if (b)
+ {
+ return y; // strictly positive
+ }
+
+ return 0;
+ }
+
+ void Inconsistent()
+ {
+ var i = 1;
+ if (i < 0)
+ {
+ System.Console.WriteLine(i); // reported as strictly pos, although unreachable
+ }
+ }
+
+ void SpecialValues(int[] ints)
+ {
+ System.Console.WriteLine(ints.Length); // positive
+ ints = new int[] { 1, 2, 3 };
+ System.Console.WriteLine(ints.Length); // 3, so strictly positive
+ System.Console.WriteLine(ints.Count()); // positive
+ System.Console.WriteLine(ints.Count(i => i > 1)); // positive
+
+ var s = "abc";
+ System.Console.WriteLine(s.Length); // positive, could be strictly positive
+
+ var enumerable = Enumerable.Empty();
+ System.Console.WriteLine(enumerable.Count()); // positive
+
+ var i = new int[,] { { 1, 1 }, { 1, 2 }, { 1, 3 } };
+ System.Console.WriteLine(i.Length); // 6, so strictly positive
+ }
+
+ void Phi1(int i)
+ {
+ if (i > 0)
+ {
+ System.Console.WriteLine(i); // strictly positive
+ }
+ else
+ {
+ System.Console.WriteLine(i); // negative
+ }
+ System.Console.WriteLine(i); // any
+ }
+
+ void Phi2(int i)
+ {
+ if (i > 0)
+ {
+ System.Console.WriteLine(i); // strictly positive
+ }
+ else
+ {
+ if (i < 0) // negative
+ {
+ System.Console.WriteLine(i); // strictly negative
+ return;
+ }
+ }
+ System.Console.WriteLine(i); // positive, not found
+ }
+
+ void Phi3(int i)
+ {
+ if (i > 0)
+ {
+ System.Console.WriteLine(i); // strictly positive
+ }
+ else
+ {
+ if (i < 0) // negative
+ {
+ System.Console.WriteLine(i); // strictly negative
+ }
+ else
+ {
+ System.Console.WriteLine(i); // zero, nothing is reported
+ }
+ }
+ }
+
+ void Loop(int i, int j, int k)
+ {
+ if (i > 0)
+ {
+ while (i >= 0) // any
+ {
+ i--; // positive
+ System.Console.WriteLine(i); // any
+ }
+ System.Console.WriteLine(i); // strictly neg
+ }
+
+ if (j > 0)
+ {
+ while (j > 0)
+ {
+ j--; // strictly pos
+ System.Console.WriteLine(j); // positive
+ }
+ System.Console.WriteLine(j); // reported negative, can only be 0
+ }
+
+ if (k > 0)
+ {
+ while (k > 0)
+ {
+ k--; // strictly pos
+ System.Console.WriteLine(k); // positive
+
+ if (k == 5) // positive
+ {
+ break;
+ }
+ }
+ System.Console.WriteLine(k); // any
+ }
+ }
+
+ void Assert(int i, bool b)
+ {
+ Debug.Assert(i > 0);
+ System.Console.WriteLine(i); // strictly positive
+
+ if (b)
+ System.Console.WriteLine(i); // strictly positive
+ }
+
+ void CheckedUnchecked(int i)
+ {
+ var x = unchecked(-1 * i * i);
+ if (x < 0)
+ {
+ System.Console.WriteLine(x); // strictly negative
+ }
+
+ x = checked(-1 * i * i);
+ if (x < 0)
+ {
+ System.Console.WriteLine(x); // strictly negative
+ }
+ }
+
+ void CharMinMax()
+ {
+ var min = char.MinValue;
+ var max = char.MaxValue;
+ var c = min + 1;
+ System.Console.WriteLine(c); // strictly positive
+ c = min - 1;
+ System.Console.WriteLine(c); // strictly negative
+ c = max + 1;
+ System.Console.WriteLine(c); // strictly positive
+ }
+
+ void NullCoalesce(int? v)
+ {
+ if (v > 0)
+ {
+ var x = v ?? 1;
+ System.Console.WriteLine(x); // strictly positive
+ }
+
+ if (v == null)
+ {
+ var x = v ?? 1;
+ System.Console.WriteLine(x); // strictly positive
+ }
+
+ if (v < 0)
+ {
+ var x = v ?? 0;
+ System.Console.WriteLine(x); // negative
+ }
+ }
+
+ async System.Threading.Tasks.Task Await()
+ {
+ var i = await System.Threading.Tasks.Task.FromResult(5);
+ if (i < 0)
+ {
+ System.Console.WriteLine(i); // strictly negative
+ }
+ }
+
+ void Unsigned(uint i)
+ {
+ if (i != 0) // positive
+ {
+ System.Console.WriteLine(i); // strictly positive
+ }
+ }
+
+ public int MyField = 0;
+
+ void FieldAccess()
+ {
+ var x = new SignAnalysis();
+ var y = x.MyField;
+ if (y < 0)
+ {
+ System.Console.WriteLine(y); // strictly negative
+ }
+ }
+
+ private static unsafe void Pointer(float d)
+ {
+ float* dp = &d;
+ var x = *dp;
+ if (x < 0)
+ {
+ System.Console.WriteLine(x); // strictly negative
+ }
+ }
+
+ [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit, Size = 15)]
+ struct MyStruct { }
+
+ unsafe void Sizeof()
+ {
+ var x = sizeof(MyStruct);
+ System.Console.WriteLine(x); // strictly positive
+ }
+
+ void SwitchCase(string s)
+ {
+ var x = s switch
+ {
+ "x" => 0,
+ _ => 2
+ };
+ System.Console.WriteLine(x); // positive
+ }
+
+ void Capture()
+ {
+ var i = 1;
+ void Capture()
+ {
+ if (i > 0)
+ Console.WriteLine(i); // strictly positive
+ }
+ Capture();
+
+ if (i > 0)
+ Console.WriteLine(i); // strictly positive
+ }
+
+ public struct MyStruct2 { public int F; }
+ void RefExpression(MyStruct2 s)
+ {
+ ref var x = ref s.F;
+ if (x < 0)
+ {
+ Console.WriteLine(x); // strictly negative
+ }
+ }
+
+ enum MyEnum { A = 12, B, C }
+ void EnumOp(MyEnum x, MyEnum y)
+ {
+ var i = x - y;
+ if (i < 0)
+ {
+ System.Console.WriteLine(i); // strictly negative
+ }
+ }
+
+ unsafe void PointerCast(byte* src, byte* dst)
+ {
+ var x = (int)(src-dst);
+ if (x < 0)
+ {
+ System.Console.WriteLine(x); // strictly negative
+ }
+
+ byte[] buf = new byte[10];
+
+ fixed (byte* to = buf)
+ {
+ System.Console.WriteLine((int)to);
+ }
+ }
+}
+
+// semmle-extractor-options: /r:System.Linq.dll
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
new file mode 100644
index 00000000000..91d429a45ef
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
@@ -0,0 +1,217 @@
+| SignAnalysis.cs:7:72:7:75 | 1000 | strictlyPositive |
+| SignAnalysis.cs:7:80:7:82 | 500 | strictlyPositive |
+| SignAnalysis.cs:18:20:18:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:24:20:24:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:30:20:30:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:36:20:36:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:42:20:42:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:45:24:45:24 | 1 | strictlyPositive |
+| SignAnalysis.cs:49:20:49:20 | access to local variable v | strictlyNegative |
+| SignAnalysis.cs:59:17:59:25 | Int32 x = ... | strictlyNegative |
+| SignAnalysis.cs:59:21:59:21 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:59:21:59:25 | ... + ... | strictlyNegative |
+| SignAnalysis.cs:59:25:59:25 | access to parameter j | strictlyNegative |
+| SignAnalysis.cs:60:38:60:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:61:13:61:21 | ... = ... | strictlyPositive |
+| SignAnalysis.cs:61:17:61:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:61:17:61:21 | ... * ... | strictlyPositive |
+| SignAnalysis.cs:61:21:61:21 | access to parameter j | strictlyNegative |
+| SignAnalysis.cs:62:38:62:38 | access to local variable x | strictlyPositive |
+| SignAnalysis.cs:63:13:63:21 | ... = ... | positive |
+| SignAnalysis.cs:63:17:63:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:63:17:63:21 | ... / ... | positive |
+| SignAnalysis.cs:63:21:63:21 | access to parameter j | strictlyNegative |
+| SignAnalysis.cs:64:38:64:38 | access to local variable x | positive |
+| SignAnalysis.cs:65:17:65:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:65:21:65:21 | access to parameter j | strictlyNegative |
+| SignAnalysis.cs:67:13:67:21 | ... = ... | negative |
+| SignAnalysis.cs:67:17:67:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:67:17:67:21 | ... % ... | negative |
+| SignAnalysis.cs:67:21:67:21 | access to parameter j | strictlyNegative |
+| SignAnalysis.cs:68:38:68:38 | access to local variable x | negative |
+| SignAnalysis.cs:69:13:69:19 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:69:17:69:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:69:17:69:19 | ...++ | strictlyNegative |
+| SignAnalysis.cs:70:38:70:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:71:13:71:19 | ... = ... | negative |
+| SignAnalysis.cs:71:17:71:17 | access to parameter i | negative |
+| SignAnalysis.cs:71:17:71:19 | ...-- | negative |
+| SignAnalysis.cs:72:38:72:38 | access to local variable x | negative |
+| SignAnalysis.cs:73:13:73:18 | ... = ... | strictlyPositive |
+| SignAnalysis.cs:73:17:73:18 | -... | strictlyPositive |
+| SignAnalysis.cs:73:18:73:18 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:74:38:74:38 | access to local variable x | strictlyPositive |
+| SignAnalysis.cs:75:13:75:18 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:75:17:75:18 | +... | strictlyNegative |
+| SignAnalysis.cs:75:18:75:18 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:76:38:76:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:77:17:77:27 | Int64 l = ... | strictlyNegative |
+| SignAnalysis.cs:77:21:77:27 | (...) ... | strictlyNegative |
+| SignAnalysis.cs:77:27:77:27 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:78:38:78:38 | access to local variable l | strictlyNegative |
+| SignAnalysis.cs:80:13:80:17 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:80:17:80:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:81:13:81:13 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:81:13:81:18 | ... + ... | strictlyNegative |
+| SignAnalysis.cs:81:13:81:18 | ... += ... | strictlyNegative |
+| SignAnalysis.cs:81:13:81:18 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:81:18:81:18 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:82:38:82:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:87:21:87:21 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:87:25:87:25 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:89:13:89:21 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:89:17:89:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:89:17:89:21 | ... * ... | strictlyNegative |
+| SignAnalysis.cs:89:21:89:21 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:90:38:90:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:91:13:91:21 | ... = ... | negative |
+| SignAnalysis.cs:91:17:91:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:91:17:91:21 | ... / ... | negative |
+| SignAnalysis.cs:91:21:91:21 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:92:38:92:38 | access to local variable x | negative |
+| SignAnalysis.cs:93:13:93:21 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:93:17:93:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:93:17:93:21 | ... - ... | strictlyNegative |
+| SignAnalysis.cs:93:21:93:21 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:94:38:94:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:95:13:95:21 | ... = ... | negative |
+| SignAnalysis.cs:95:17:95:17 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:95:17:95:21 | ... % ... | negative |
+| SignAnalysis.cs:95:21:95:21 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:96:38:96:38 | access to local variable x | negative |
+| SignAnalysis.cs:97:21:97:21 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:97:25:97:25 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:104:13:104:20 | Single f = ... | strictlyPositive |
+| SignAnalysis.cs:104:17:104:20 | 4.2 | strictlyPositive |
+| SignAnalysis.cs:105:34:105:34 | access to local variable f | strictlyPositive |
+| SignAnalysis.cs:106:13:106:19 | Double d = ... | strictlyPositive |
+| SignAnalysis.cs:106:17:106:19 | 4.2 | strictlyPositive |
+| SignAnalysis.cs:107:34:107:34 | access to local variable d | strictlyPositive |
+| SignAnalysis.cs:108:13:108:21 | Decimal de = ... | strictlyPositive |
+| SignAnalysis.cs:108:18:108:21 | 4.2 | strictlyPositive |
+| SignAnalysis.cs:109:34:109:35 | access to local variable de | strictlyPositive |
+| SignAnalysis.cs:110:13:110:19 | Char c = ... | strictlyPositive |
+| SignAnalysis.cs:110:17:110:19 | a | strictlyPositive |
+| SignAnalysis.cs:111:34:111:34 | access to local variable c | strictlyPositive |
+| SignAnalysis.cs:120:9:120:10 | access to field f0 | positive |
+| SignAnalysis.cs:120:9:120:12 | ...++ | positive |
+| SignAnalysis.cs:121:34:121:35 | access to field f0 | strictlyPositive |
+| SignAnalysis.cs:122:9:122:10 | access to field f0 | positive |
+| SignAnalysis.cs:129:9:129:16 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:129:14:129:16 | -... | strictlyNegative |
+| SignAnalysis.cs:129:15:129:16 | 10 | strictlyPositive |
+| SignAnalysis.cs:141:34:141:34 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:148:20:148:20 | access to parameter x | strictlyNegative |
+| SignAnalysis.cs:151:18:151:18 | 1 | strictlyPositive |
+| SignAnalysis.cs:153:20:153:20 | access to parameter y | strictlyPositive |
+| SignAnalysis.cs:156:18:156:19 | -... | strictlyNegative |
+| SignAnalysis.cs:156:19:156:19 | 1 | strictlyPositive |
+| SignAnalysis.cs:161:13:161:13 | access to parameter x | positive |
+| SignAnalysis.cs:163:20:163:20 | access to parameter y | strictlyPositive |
+| SignAnalysis.cs:166:22:166:22 | 1 | strictlyPositive |
+| SignAnalysis.cs:169:20:169:20 | access to parameter y | strictlyPositive |
+| SignAnalysis.cs:177:13:177:17 | Int32 i = ... | strictlyPositive |
+| SignAnalysis.cs:177:17:177:17 | 1 | strictlyPositive |
+| SignAnalysis.cs:178:13:178:13 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:180:38:180:38 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:186:34:186:44 | access to property Length | positive |
+| SignAnalysis.cs:187:16:187:36 | 3 | strictlyPositive |
+| SignAnalysis.cs:187:28:187:28 | 1 | strictlyPositive |
+| SignAnalysis.cs:187:31:187:31 | 2 | strictlyPositive |
+| SignAnalysis.cs:187:34:187:34 | 3 | strictlyPositive |
+| SignAnalysis.cs:188:34:188:44 | access to property Length | strictlyPositive |
+| SignAnalysis.cs:189:34:189:45 | call to method Count | positive |
+| SignAnalysis.cs:190:34:190:55 | call to method Count | positive |
+| SignAnalysis.cs:190:54:190:54 | 1 | strictlyPositive |
+| SignAnalysis.cs:193:34:193:41 | access to property Length | positive |
+| SignAnalysis.cs:196:34:196:51 | call to method Count | positive |
+| SignAnalysis.cs:198:17:198:59 | 2 | strictlyPositive |
+| SignAnalysis.cs:198:17:198:59 | 3 | strictlyPositive |
+| SignAnalysis.cs:198:32:198:32 | 1 | strictlyPositive |
+| SignAnalysis.cs:198:35:198:35 | 1 | strictlyPositive |
+| SignAnalysis.cs:198:42:198:42 | 1 | strictlyPositive |
+| SignAnalysis.cs:198:45:198:45 | 2 | strictlyPositive |
+| SignAnalysis.cs:198:52:198:52 | 1 | strictlyPositive |
+| SignAnalysis.cs:198:55:198:55 | 3 | strictlyPositive |
+| SignAnalysis.cs:199:34:199:41 | access to property Length | strictlyPositive |
+| SignAnalysis.cs:206:38:206:38 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:210:38:210:38 | access to parameter i | negative |
+| SignAnalysis.cs:219:38:219:38 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:223:17:223:17 | access to parameter i | negative |
+| SignAnalysis.cs:225:42:225:42 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:236:38:236:38 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:240:17:240:17 | access to parameter i | negative |
+| SignAnalysis.cs:242:42:242:42 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:257:17:257:17 | access to parameter i | positive |
+| SignAnalysis.cs:257:17:257:19 | ...-- | positive |
+| SignAnalysis.cs:260:38:260:38 | access to parameter i | strictlyNegative |
+| SignAnalysis.cs:267:17:267:17 | access to parameter j | strictlyPositive |
+| SignAnalysis.cs:267:17:267:19 | ...-- | strictlyPositive |
+| SignAnalysis.cs:268:42:268:42 | access to parameter j | positive |
+| SignAnalysis.cs:270:38:270:38 | access to parameter j | negative |
+| SignAnalysis.cs:277:17:277:17 | access to parameter k | strictlyPositive |
+| SignAnalysis.cs:277:17:277:19 | ...-- | strictlyPositive |
+| SignAnalysis.cs:278:42:278:42 | access to parameter k | positive |
+| SignAnalysis.cs:280:21:280:21 | access to parameter k | positive |
+| SignAnalysis.cs:280:26:280:26 | 5 | strictlyPositive |
+| SignAnalysis.cs:291:22:291:22 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:292:34:292:34 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:295:38:295:38 | access to parameter i | strictlyPositive |
+| SignAnalysis.cs:300:27:300:28 | -... | strictlyNegative |
+| SignAnalysis.cs:300:28:300:28 | 1 | strictlyPositive |
+| SignAnalysis.cs:303:38:303:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:306:21:306:22 | -... | strictlyNegative |
+| SignAnalysis.cs:306:22:306:22 | 1 | strictlyPositive |
+| SignAnalysis.cs:309:38:309:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:316:13:316:31 | Char max = ... | strictlyPositive |
+| SignAnalysis.cs:316:19:316:31 | access to constant MaxValue | strictlyPositive |
+| SignAnalysis.cs:317:13:317:23 | Int32 c = ... | strictlyPositive |
+| SignAnalysis.cs:317:17:317:23 | ... + ... | strictlyPositive |
+| SignAnalysis.cs:317:23:317:23 | 1 | strictlyPositive |
+| SignAnalysis.cs:318:34:318:34 | access to local variable c | strictlyPositive |
+| SignAnalysis.cs:319:9:319:19 | ... = ... | strictlyNegative |
+| SignAnalysis.cs:319:13:319:19 | ... - ... | strictlyNegative |
+| SignAnalysis.cs:319:19:319:19 | 1 | strictlyPositive |
+| SignAnalysis.cs:320:34:320:34 | access to local variable c | strictlyNegative |
+| SignAnalysis.cs:321:9:321:19 | ... = ... | strictlyPositive |
+| SignAnalysis.cs:321:13:321:15 | (...) ... | strictlyPositive |
+| SignAnalysis.cs:321:13:321:15 | access to local variable max | strictlyPositive |
+| SignAnalysis.cs:321:13:321:19 | ... + ... | strictlyPositive |
+| SignAnalysis.cs:321:19:321:19 | 1 | strictlyPositive |
+| SignAnalysis.cs:322:34:322:34 | access to local variable c | strictlyPositive |
+| SignAnalysis.cs:329:17:329:26 | Int32 x = ... | strictlyPositive |
+| SignAnalysis.cs:329:21:329:21 | access to parameter v | strictlyPositive |
+| SignAnalysis.cs:329:21:329:26 | ... ?? ... | strictlyPositive |
+| SignAnalysis.cs:329:26:329:26 | 1 | strictlyPositive |
+| SignAnalysis.cs:330:38:330:38 | access to local variable x | strictlyPositive |
+| SignAnalysis.cs:335:17:335:26 | Int32 x = ... | strictlyPositive |
+| SignAnalysis.cs:335:21:335:26 | ... ?? ... | strictlyPositive |
+| SignAnalysis.cs:335:26:335:26 | 1 | strictlyPositive |
+| SignAnalysis.cs:336:38:336:38 | access to local variable x | strictlyPositive |
+| SignAnalysis.cs:341:17:341:26 | Int32 x = ... | negative |
+| SignAnalysis.cs:341:21:341:21 | access to parameter v | strictlyNegative |
+| SignAnalysis.cs:341:21:341:26 | ... ?? ... | negative |
+| SignAnalysis.cs:342:38:342:38 | access to local variable x | negative |
+| SignAnalysis.cs:348:62:348:62 | 5 | strictlyPositive |
+| SignAnalysis.cs:351:38:351:38 | access to local variable i | strictlyNegative |
+| SignAnalysis.cs:371:38:371:38 | access to local variable y | strictlyNegative |
+| SignAnalysis.cs:381:38:381:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:385:50:385:99 | access to constant Explicit | strictlyPositive |
+| SignAnalysis.cs:385:109:385:110 | 15 | strictlyPositive |
+| SignAnalysis.cs:390:13:390:32 | Int32 x = ... | strictlyPositive |
+| SignAnalysis.cs:390:17:390:32 | sizeof(..) | strictlyPositive |
+| SignAnalysis.cs:391:34:391:34 | access to local variable x | strictlyPositive |
+| SignAnalysis.cs:396:13:400:9 | Int32 x = ... | positive |
+| SignAnalysis.cs:396:17:400:9 | ... switch { ... } | positive |
+| SignAnalysis.cs:399:13:399:18 | ... => ... | strictlyPositive |
+| SignAnalysis.cs:399:18:399:18 | 2 | strictlyPositive |
+| SignAnalysis.cs:401:34:401:34 | access to local variable x | positive |
+| SignAnalysis.cs:406:13:406:17 | Int32 i = ... | strictlyPositive |
+| SignAnalysis.cs:406:17:406:17 | 1 | strictlyPositive |
+| SignAnalysis.cs:410:35:410:35 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:414:13:414:13 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:415:31:415:31 | access to local variable i | strictlyPositive |
+| SignAnalysis.cs:424:31:424:31 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:434:38:434:38 | access to local variable i | strictlyNegative |
+| SignAnalysis.cs:443:38:443:38 | access to local variable x | strictlyNegative |
+| SignAnalysis.cs:446:31:446:32 | 10 | strictlyPositive |
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql
new file mode 100644
index 00000000000..4350e8f1742
--- /dev/null
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql
@@ -0,0 +1,21 @@
+import csharp
+import semmle.code.csharp.dataflow.SignAnalysis
+
+string getASignString(Expr e) {
+ positive(e) and
+ not strictlyPositive(e) and
+ result = "positive"
+ or
+ negative(e) and
+ not strictlyNegative(e) and
+ result = "negative"
+ or
+ strictlyPositive(e) and
+ result = "strictlyPositive"
+ or
+ strictlyNegative(e) and
+ result = "strictlyNegative"
+}
+
+from Expr e
+select e, strictconcat(string s | s = getASignString(e) | s, " ")
diff --git a/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll
index 1fb3eb201a8..b153e9fbd04 100644
--- a/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll
+++ b/java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll
@@ -6,6 +6,7 @@
import java
private import SSA
+private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon
private import RangeUtils
private import semmle.code.java.controlflow.Guards
import Bound
diff --git a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll
index cbd59f8302a..07860c76ee4 100644
--- a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll
+++ b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll
@@ -66,6 +66,7 @@
import java
private import SSA
private import RangeUtils
+private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon
private import semmle.code.java.controlflow.internal.GuardsLogic
private import SignAnalysis
private import ModulusAnalysis
diff --git a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll
index e7da9891b47..631145a2aa2 100644
--- a/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll
+++ b/java/ql/src/semmle/code/java/dataflow/RangeUtils.qll
@@ -5,6 +5,7 @@
import java
private import SSA
private import semmle.code.java.controlflow.internal.GuardsLogic
+private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon
/**
* Holds if `v` is an input to `phi` that is not along a back edge, and the
@@ -127,63 +128,6 @@ Expr ssaRead(SsaVariable v, int delta) {
result.(AssignExpr).getSource() = ssaRead(v, delta)
}
-private newtype TSsaReadPosition =
- TSsaReadPositionBlock(BasicBlock bb) { exists(SsaVariable v | bb = v.getAUse().getBasicBlock()) } or
- TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) {
- exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock())
- }
-
-/**
- * A position at which an SSA variable is read. This includes both ordinary
- * reads occurring in basic blocks and input to phi nodes occurring along an
- * edge between two basic blocks.
- */
-class SsaReadPosition extends TSsaReadPosition {
- /** Holds if `v` is read at this position. */
- abstract predicate hasReadOfVar(SsaVariable v);
-
- /** Gets a textual representation of this SSA read position. */
- abstract string toString();
-}
-
-/** A basic block in which an SSA variable is read. */
-class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock {
- /** Gets the basic block corresponding to this position. */
- BasicBlock getBlock() { this = TSsaReadPositionBlock(result) }
-
- override predicate hasReadOfVar(SsaVariable v) { getBlock() = v.getAUse().getBasicBlock() }
-
- override string toString() { result = "block" }
-}
-
-/**
- * An edge between two basic blocks where the latter block has an SSA phi
- * definition. The edge therefore has a read of an SSA variable serving as the
- * input to the phi node.
- */
-class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge {
- /** Gets the head of the edge. */
- BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) }
-
- /** Gets the tail of the edge. */
- BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) }
-
- override predicate hasReadOfVar(SsaVariable v) {
- exists(SsaPhiNode phi |
- phi.hasInputFromBlock(v, getOrigBlock()) and
- getPhiBlock() = phi.getBasicBlock()
- )
- }
-
- /** Holds if `inp` is an input to `phi` along this edge. */
- predicate phiInput(SsaPhiNode phi, SsaVariable inp) {
- phi.hasInputFromBlock(inp, getOrigBlock()) and
- getPhiBlock() = phi.getBasicBlock()
- }
-
- override string toString() { result = "edge" }
-}
-
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
diff --git a/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll
index 595f9c623f2..9cd629f4ef9 100644
--- a/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll
+++ b/java/ql/src/semmle/code/java/dataflow/SignAnalysis.qll
@@ -6,596 +6,4 @@
* three-valued domain `{negative, zero, positive}`.
*/
-import java
-private import SSA
-private import RangeUtils
-private import semmle.code.java.controlflow.Guards
-private import semmle.code.java.Reflection
-private import semmle.code.java.Collections
-private import semmle.code.java.Maps
-
-private newtype TSign =
- TNeg() or
- TZero() or
- TPos()
-
-private class Sign extends TSign {
- string toString() {
- result = "-" and this = TNeg()
- or
- result = "0" and this = TZero()
- or
- result = "+" and this = TPos()
- }
-
- Sign inc() {
- this = TNeg() and result = TNeg()
- or
- this = TNeg() and result = TZero()
- or
- this = TZero() and result = TPos()
- or
- this = TPos() and result = TPos()
- }
-
- Sign dec() { result.inc() = this }
-
- Sign neg() {
- this = TNeg() and result = TPos()
- or
- this = TZero() and result = TZero()
- or
- this = TPos() and result = TNeg()
- }
-
- Sign bitnot() {
- this = TNeg() and result = TPos()
- or
- this = TNeg() and result = TZero()
- or
- this = TZero() and result = TNeg()
- or
- this = TPos() and result = TNeg()
- }
-
- Sign add(Sign s) {
- this = TZero() and result = s
- or
- s = TZero() and result = this
- or
- this = s and this = result
- or
- this = TPos() and s = TNeg()
- or
- this = TNeg() and s = TPos()
- }
-
- Sign mul(Sign s) {
- result = TZero() and this = TZero()
- or
- result = TZero() and s = TZero()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign div(Sign s) {
- result = TZero() and s = TNeg()
- or
- result = TZero() and s = TPos()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign rem(Sign s) {
- result = TZero() and s = TNeg()
- or
- result = TZero() and s = TPos()
- or
- result = this and s = TNeg()
- or
- result = this and s = TPos()
- }
-
- Sign bitand(Sign s) {
- result = TZero() and this = TZero()
- or
- result = TZero() and s = TZero()
- or
- result = TZero() and this = TPos()
- or
- result = TZero() and s = TPos()
- or
- result = TNeg() and this = TNeg() and s = TNeg()
- or
- result = TPos() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TNeg()
- or
- result = TPos() and this = TPos() and s = TPos()
- }
-
- Sign bitor(Sign s) {
- result = TZero() and this = TZero() and s = TZero()
- or
- result = TNeg() and this = TNeg()
- or
- result = TNeg() and s = TNeg()
- or
- result = TPos() and this = TPos() and s = TZero()
- or
- result = TPos() and this = TZero() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- }
-
- Sign bitxor(Sign s) {
- result = TZero() and this = s
- or
- result = this and s = TZero()
- or
- result = s and this = TZero()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign lshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- this != TZero() and s != TZero()
- }
-
- Sign rshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- result = TNeg() and this = TNeg()
- or
- result != TNeg() and this = TPos() and s != TZero()
- }
-
- Sign urshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- result != TZero() and this = TNeg() and s != TZero()
- or
- result != TNeg() and this = TPos() and s != TZero()
- }
-}
-
-/** Gets the sign of `e` if this can be directly determined. */
-private Sign certainExprSign(Expr e) {
- exists(int i | e.(ConstantIntegerExpr).getIntValue() = i |
- i < 0 and result = TNeg()
- or
- i = 0 and result = TZero()
- or
- i > 0 and result = TPos()
- )
- or
- not exists(e.(ConstantIntegerExpr).getIntValue()) and
- (
- exists(float f |
- f = e.(LongLiteral).getValue().toFloat() or
- f = e.(FloatingPointLiteral).getValue().toFloat() or
- f = e.(DoubleLiteral).getValue().toFloat()
- |
- f < 0 and result = TNeg()
- or
- f = 0 and result = TZero()
- or
- f > 0 and result = TPos()
- )
- or
- exists(string charlit | charlit = e.(CharacterLiteral).getValue() |
- if charlit = "\\0" or charlit = "\\u0000" then result = TZero() else result = TPos()
- )
- or
- e.(MethodAccess).getMethod() instanceof StringLengthMethod and
- (result = TPos() or result = TZero())
- or
- e.(MethodAccess).getMethod() instanceof CollectionSizeMethod and
- (result = TPos() or result = TZero())
- or
- e.(MethodAccess).getMethod() instanceof MapSizeMethod and
- (result = TPos() or result = TZero())
- )
-}
-
-/** Holds if the sign of `e` is too complicated to determine. */
-private predicate unknownSign(Expr e) {
- not exists(e.(ConstantIntegerExpr).getIntValue()) and
- (
- exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt()))
- or
- exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
- or
- exists(CastExpr cast, Type fromtyp |
- cast = e and
- fromtyp = cast.getExpr().getType() and
- not fromtyp instanceof NumericOrCharType
- )
- or
- e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType
- or
- e instanceof MethodAccess and e.getType() instanceof NumericOrCharType
- or
- e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType
- )
-}
-
-/**
- * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
- * to only include bounds for which we might determine a sign.
- */
-private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
- exists(boolean testIsTrue, ComparisonExpr comp |
- pos.hasReadOfVar(v) and
- guardControlsSsaRead(comp, pos, testIsTrue) and
- not unknownSign(lowerbound)
- |
- testIsTrue = true and
- comp.getLesserOperand() = lowerbound and
- comp.getGreaterOperand() = ssaRead(v, 0) and
- (if comp.isStrict() then isStrict = true else isStrict = false)
- or
- testIsTrue = false and
- comp.getGreaterOperand() = lowerbound and
- comp.getLesserOperand() = ssaRead(v, 0) and
- (if comp.isStrict() then isStrict = false else isStrict = true)
- )
-}
-
-/**
- * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
- * to only include bounds for which we might determine a sign.
- */
-private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
- exists(boolean testIsTrue, ComparisonExpr comp |
- pos.hasReadOfVar(v) and
- guardControlsSsaRead(comp, pos, testIsTrue) and
- not unknownSign(upperbound)
- |
- testIsTrue = true and
- comp.getGreaterOperand() = upperbound and
- comp.getLesserOperand() = ssaRead(v, 0) and
- (if comp.isStrict() then isStrict = true else isStrict = false)
- or
- testIsTrue = false and
- comp.getLesserOperand() = upperbound and
- comp.getGreaterOperand() = ssaRead(v, 0) and
- (if comp.isStrict() then isStrict = false else isStrict = true)
- )
-}
-
-/**
- * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
- * restricted to only include bounds for which we might determine a sign. The
- * boolean `isEq` gives the polarity:
- * - `isEq = true` : `v = eqbound`
- * - `isEq = false` : `v != eqbound`
- */
-private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) {
- exists(Guard guard, boolean testIsTrue, boolean polarity |
- pos.hasReadOfVar(v) and
- guardControlsSsaRead(guard, pos, testIsTrue) and
- guard.isEquality(eqbound, ssaRead(v, 0), polarity) and
- isEq = polarity.booleanXor(testIsTrue).booleanNot() and
- not unknownSign(eqbound)
- )
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
- * order for `v` to be positive.
- */
-private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
- upperBound(bound, v, pos, _) or
- eqBound(bound, v, pos, true)
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
- * order for `v` to be negative.
- */
-private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
- lowerBound(bound, v, pos, _) or
- eqBound(bound, v, pos, true)
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
- * can be zero.
- */
-private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
- lowerBound(bound, v, pos, _) or
- upperBound(bound, v, pos, _) or
- eqBound(bound, v, pos, _)
-}
-
-/** Holds if `bound` allows `v` to be positive at `pos`. */
-private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
- posBound(bound, v, pos) and TPos() = exprSign(bound)
-}
-
-/** Holds if `bound` allows `v` to be negative at `pos`. */
-private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
- negBound(bound, v, pos) and TNeg() = exprSign(bound)
-}
-
-/** Holds if `bound` allows `v` to be zero at `pos`. */
-private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
- lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound)
- or
- lowerBound(bound, v, pos, false) and TZero() = exprSign(bound)
- or
- upperBound(bound, v, pos, _) and TPos() = exprSign(bound)
- or
- upperBound(bound, v, pos, false) and TZero() = exprSign(bound)
- or
- eqBound(bound, v, pos, true) and TZero() = exprSign(bound)
- or
- eqBound(bound, v, pos, false) and TZero() != exprSign(bound)
-}
-
-/**
- * Holds if there is a bound that might restrict whether `v` has the sign `s`
- * at `pos`.
- */
-private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) {
- s = TPos() and posBound(_, v, pos)
- or
- s = TNeg() and negBound(_, v, pos)
- or
- s = TZero() and zeroBound(_, v, pos)
-}
-
-pragma[noinline]
-private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) {
- result = ssaDefSign(v) and
- pos.hasReadOfVar(v) and
- hasGuard(v, pos, result)
-}
-
-pragma[noinline]
-private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) {
- result = ssaDefSign(v) and
- pos.hasReadOfVar(v) and
- not hasGuard(v, pos, result)
-}
-
-private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
- result = TPos() and
- forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
- or
- result = TNeg() and
- forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
- or
- result = TZero() and
- forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
-}
-
-/** Gets a possible sign for `v` at `pos`. */
-private Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
- result = unguardedSsaSign(v, pos)
- or
- result = guardedSsaSign(v, pos) and
- result = guardedSsaSignOk(v, pos)
-}
-
-/** Gets a possible sign for `v`. */
-pragma[nomagic]
-private Sign ssaDefSign(SsaVariable v) {
- exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() |
- result = exprSign(def.(VariableAssign).getSource())
- or
- exists(EnhancedForStmt for | def = for.getVariable())
- or
- result = exprSign(def.(PostIncExpr).getExpr()).inc()
- or
- result = exprSign(def.(PreIncExpr).getExpr()).inc()
- or
- result = exprSign(def.(PostDecExpr).getExpr()).dec()
- or
- result = exprSign(def.(PreDecExpr).getExpr()).dec()
- or
- exists(AssignOp a | a = def and result = exprSign(a))
- )
- or
- result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable())
- or
- result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable())
- or
- exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p))
- or
- exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge |
- v = phi and
- edge.phiInput(phi, inp) and
- result = ssaSign(inp, edge)
- )
-}
-
-/** Gets a possible sign for `f`. */
-private Sign fieldSign(Field f) {
- result = exprSign(f.getAnAssignedValue())
- or
- exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
- or
- exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
- or
- exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
- or
- exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
- or
- exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a))
- or
- exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f)
- or
- if f.fromSource()
- then not exists(f.getInitializer()) and result = TZero()
- else
- if f instanceof ArrayLengthField
- then result != TNeg()
- else
- if f.hasName("MAX_VALUE")
- then result = TPos()
- else
- if f.hasName("MIN_VALUE")
- then result = TNeg()
- else any()
-}
-
-/** Gets a possible sign for `e`. */
-cached
-private Sign exprSign(Expr e) {
- result = certainExprSign(e)
- or
- not exists(certainExprSign(e)) and
- (
- unknownSign(e)
- or
- exists(SsaVariable v | v.getAUse() = e |
- result = ssaSign(v, any(SsaReadPositionBlock bb | bb.getBlock() = e.getBasicBlock()))
- or
- not exists(e.getBasicBlock()) and result = ssaDefSign(v)
- )
- or
- exists(FieldAccess fa | fa = e |
- not exists(SsaVariable v | v.getAUse() = fa) and
- result = fieldSign(fa.getField())
- )
- or
- exists(VarAccess va | va = e |
- not exists(SsaVariable v | v.getAUse() = va) and
- not va instanceof FieldAccess
- )
- or
- result = exprSign(e.(AssignExpr).getSource())
- or
- result = exprSign(e.(PlusExpr).getExpr())
- or
- result = exprSign(e.(PostIncExpr).getExpr())
- or
- result = exprSign(e.(PostDecExpr).getExpr())
- or
- result = exprSign(e.(PreIncExpr).getExpr()).inc()
- or
- result = exprSign(e.(PreDecExpr).getExpr()).dec()
- or
- result = exprSign(e.(MinusExpr).getExpr()).neg()
- or
- result = exprSign(e.(BitNotExpr).getExpr()).bitnot()
- or
- exists(DivExpr div |
- div = e and
- result = exprSign(div.getLeftOperand()) and
- result != TZero()
- |
- div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or
- div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0
- )
- or
- exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) |
- (e instanceof AssignAddExpr or e instanceof AddExpr) and
- result = s1.add(s2)
- or
- (e instanceof AssignSubExpr or e instanceof SubExpr) and
- result = s1.add(s2.neg())
- or
- (e instanceof AssignMulExpr or e instanceof MulExpr) and
- result = s1.mul(s2)
- or
- (e instanceof AssignDivExpr or e instanceof DivExpr) and
- result = s1.div(s2)
- or
- (e instanceof AssignRemExpr or e instanceof RemExpr) and
- result = s1.rem(s2)
- or
- (e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and
- result = s1.bitand(s2)
- or
- (e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and
- result = s1.bitor(s2)
- or
- (e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and
- result = s1.bitxor(s2)
- or
- (e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and
- result = s1.lshift(s2)
- or
- (e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and
- result = s1.rshift(s2)
- or
- (e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and
- result = s1.urshift(s2)
- )
- or
- result = exprSign(e.(ChooseExpr).getAResultExpr())
- or
- result = exprSign(e.(CastExpr).getExpr())
- )
-}
-
-private Sign binaryOpLhsSign(Expr e) {
- result = exprSign(e.(BinaryExpr).getLeftOperand()) or
- result = exprSign(e.(AssignOp).getDest())
-}
-
-private Sign binaryOpRhsSign(Expr e) {
- result = exprSign(e.(BinaryExpr).getRightOperand()) or
- result = exprSign(e.(AssignOp).getRhs())
-}
-
-pragma[noinline]
-private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) {
- lhs = binaryOpLhsSign(e) and
- rhs = binaryOpRhsSign(e)
-}
-
-/** Holds if `e` can be positive and cannot be negative. */
-predicate positive(Expr e) {
- exprSign(e) = TPos() and
- not exprSign(e) = TNeg()
-}
-
-/** Holds if `e` can be negative and cannot be positive. */
-predicate negative(Expr e) {
- exprSign(e) = TNeg() and
- not exprSign(e) = TPos()
-}
-
-/** Holds if `e` is strictly positive. */
-predicate strictlyPositive(Expr e) {
- exprSign(e) = TPos() and
- not exprSign(e) = TNeg() and
- not exprSign(e) = TZero()
-}
-
-/** Holds if `e` is strictly negative. */
-predicate strictlyNegative(Expr e) {
- exprSign(e) = TNeg() and
- not exprSign(e) = TPos() and
- not exprSign(e) = TZero()
-}
+import semmle.code.java.dataflow.internal.rangeanalysis.SignAnalysisCommon
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll
new file mode 100644
index 00000000000..10ca946a044
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll
@@ -0,0 +1,219 @@
+newtype TSign =
+ TNeg() or
+ TZero() or
+ TPos()
+
+/** Class representing expression signs (+, -, 0). */
+class Sign extends TSign {
+ /** Gets the string representation of this sign. */
+ string toString() {
+ result = "-" and this = TNeg()
+ or
+ result = "0" and this = TZero()
+ or
+ result = "+" and this = TPos()
+ }
+
+ /** Gets a possible sign after incrementing an expression that has this sign. */
+ Sign inc() {
+ this = TNeg() and result = TNeg()
+ or
+ this = TNeg() and result = TZero()
+ or
+ this = TZero() and result = TPos()
+ or
+ this = TPos() and result = TPos()
+ }
+
+ /** Gets a possible sign after decrementing an expression that has this sign. */
+ Sign dec() { result.inc() = this }
+
+ /** Gets a possible sign after negating an expression that has this sign. */
+ Sign neg() {
+ this = TNeg() and result = TPos()
+ or
+ this = TZero() and result = TZero()
+ or
+ this = TPos() and result = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after bitwise complementing an expression that has this
+ * sign.
+ */
+ Sign bitnot() {
+ this = TNeg() and result = TPos()
+ or
+ this = TNeg() and result = TZero()
+ or
+ this = TZero() and result = TNeg()
+ or
+ this = TPos() and result = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after adding an expression with sign `s` to an expression
+ * that has this sign.
+ */
+ Sign add(Sign s) {
+ this = TZero() and result = s
+ or
+ s = TZero() and result = this
+ or
+ this = s and this = result
+ or
+ this = TPos() and s = TNeg()
+ or
+ this = TNeg() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after multiplying an expression with sign `s` to an expression
+ * that has this sign.
+ */
+ Sign mul(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = TZero() and s = TZero()
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after integer dividing an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign div(Sign s) {
+ result = TZero() and s = TNeg() // ex: 3 / -5 = 0
+ or
+ result = TZero() and s = TPos() // ex: 3 / 5 = 0
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after modulo dividing an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign rem(Sign s) {
+ result = TZero() and s = TNeg()
+ or
+ result = TZero() and s = TPos()
+ or
+ result = this and s = TNeg()
+ or
+ result = this and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `and` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitand(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = TZero() and s = TZero()
+ or
+ result = TZero() and this = TPos()
+ or
+ result = TZero() and s = TPos()
+ or
+ result = TNeg() and this = TNeg() and s = TNeg()
+ or
+ result = TPos() and this = TNeg() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TNeg()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `or` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitor(Sign s) {
+ result = TZero() and this = TZero() and s = TZero()
+ or
+ result = TNeg() and this = TNeg()
+ or
+ result = TNeg() and s = TNeg()
+ or
+ result = TPos() and this = TPos() and s = TZero()
+ or
+ result = TPos() and this = TZero() and s = TPos()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ }
+
+ /**
+ * Gets a possible sign after bitwise `xor` of an expression that has this sign
+ * and an expression with sign `s`.
+ */
+ Sign bitxor(Sign s) {
+ result = TZero() and this = s
+ or
+ result = this and s = TZero()
+ or
+ result = s and this = TZero()
+ or
+ result = TPos() and this = TPos() and s = TPos()
+ or
+ result = TNeg() and this = TNeg() and s = TPos()
+ or
+ result = TNeg() and this = TPos() and s = TNeg()
+ or
+ result = TPos() and this = TNeg() and s = TNeg()
+ }
+
+ /**
+ * Gets a possible sign after left shift of an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign lshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ this != TZero() and s != TZero()
+ }
+
+ /**
+ * Gets a possible sign after right shift of an expression that has this sign
+ * by an expression with sign `s`.
+ */
+ Sign rshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ result = TNeg() and this = TNeg()
+ or
+ result != TNeg() and this = TPos() and s != TZero()
+ }
+
+ /**
+ * Gets a possible sign after unsigned right shift of an expression that has
+ * this sign by an expression with sign `s`.
+ */
+ Sign urshift(Sign s) {
+ result = TZero() and this = TZero()
+ or
+ result = this and s = TZero()
+ or
+ result != TZero() and this = TNeg() and s != TZero()
+ or
+ result != TNeg() and this = TPos() and s != TZero()
+ }
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
new file mode 100644
index 00000000000..66b38433eef
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll
@@ -0,0 +1,293 @@
+/**
+ * Provides sign analysis to determine whether expression are always positive
+ * or negative.
+ *
+ * The analysis is implemented as an abstract interpretation over the
+ * three-valued domain `{negative, zero, positive}`.
+ */
+
+private import SignAnalysisSpecific::Private
+private import SsaReadPositionCommon
+private import Sign
+
+/** Gets the sign of `e` if this can be directly determined. */
+Sign certainExprSign(Expr e) {
+ exists(int i | e.(ConstantIntegerExpr).getIntValue() = i |
+ i < 0 and result = TNeg()
+ or
+ i = 0 and result = TZero()
+ or
+ i > 0 and result = TPos()
+ )
+ or
+ not exists(e.(ConstantIntegerExpr).getIntValue()) and
+ (
+ exists(float f | f = getNonIntegerValue(e) |
+ f < 0 and result = TNeg()
+ or
+ f = 0 and result = TZero()
+ or
+ f > 0 and result = TPos()
+ )
+ or
+ exists(string charlit | charlit = getCharValue(e) |
+ if charlit.regexpMatch("\\u0000") then result = TZero() else result = TPos()
+ )
+ or
+ containerSizeAccess(e) and
+ (result = TPos() or result = TZero())
+ or
+ positiveExpression(e) and result = TPos()
+ )
+}
+
+/** Holds if the sign of `e` is too complicated to determine. */
+predicate unknownSign(Expr e) {
+ not exists(certainExprSign(e)) and
+ (
+ exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt()))
+ or
+ exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
+ or
+ exists(CastExpr cast, Type fromtyp |
+ cast = e and
+ fromtyp = cast.getExpr().getType() and
+ not fromtyp instanceof NumericOrCharType
+ )
+ or
+ unknownIntegerAccess(e)
+ )
+}
+
+/**
+ * Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
+ * to only include bounds for which we might determine a sign.
+ */
+private predicate lowerBound(Expr lowerbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
+ exists(boolean testIsTrue, ComparisonExpr comp |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and
+ not unknownSign(lowerbound)
+ |
+ testIsTrue = true and
+ comp.getLesserOperand() = lowerbound and
+ comp.getGreaterOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = true else isStrict = false)
+ or
+ testIsTrue = false and
+ comp.getGreaterOperand() = lowerbound and
+ comp.getLesserOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = false else isStrict = true)
+ )
+}
+
+/**
+ * Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
+ * to only include bounds for which we might determine a sign.
+ */
+private predicate upperBound(Expr upperbound, SsaVariable v, SsaReadPosition pos, boolean isStrict) {
+ exists(boolean testIsTrue, ComparisonExpr comp |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(getComparisonGuard(comp), pos, testIsTrue) and
+ not unknownSign(upperbound)
+ |
+ testIsTrue = true and
+ comp.getGreaterOperand() = upperbound and
+ comp.getLesserOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = true else isStrict = false)
+ or
+ testIsTrue = false and
+ comp.getLesserOperand() = upperbound and
+ comp.getGreaterOperand() = ssaRead(v, 0) and
+ (if comp.isStrict() then isStrict = false else isStrict = true)
+ )
+}
+
+/**
+ * Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
+ * restricted to only include bounds for which we might determine a sign. The
+ * boolean `isEq` gives the polarity:
+ * - `isEq = true` : `v = eqbound`
+ * - `isEq = false` : `v != eqbound`
+ */
+private predicate eqBound(Expr eqbound, SsaVariable v, SsaReadPosition pos, boolean isEq) {
+ exists(Guard guard, boolean testIsTrue, boolean polarity |
+ pos.hasReadOfVar(v) and
+ guardControlsSsaRead(guard, pos, testIsTrue) and
+ guard.isEquality(eqbound, ssaRead(v, 0), polarity) and
+ isEq = polarity.booleanXor(testIsTrue).booleanNot() and
+ not unknownSign(eqbound)
+ )
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
+ * order for `v` to be positive.
+ */
+private predicate posBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ upperBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, true)
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
+ * order for `v` to be negative.
+ */
+private predicate negBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, true)
+}
+
+/**
+ * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
+ * can be zero.
+ */
+private predicate zeroBound(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) or
+ upperBound(bound, v, pos, _) or
+ eqBound(bound, v, pos, _)
+}
+
+/** Holds if `bound` allows `v` to be positive at `pos`. */
+private predicate posBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ posBound(bound, v, pos) and TPos() = exprSign(bound)
+}
+
+/** Holds if `bound` allows `v` to be negative at `pos`. */
+private predicate negBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ negBound(bound, v, pos) and TNeg() = exprSign(bound)
+}
+
+/** Holds if `bound` allows `v` to be zero at `pos`. */
+private predicate zeroBoundOk(Expr bound, SsaVariable v, SsaReadPosition pos) {
+ lowerBound(bound, v, pos, _) and TNeg() = exprSign(bound)
+ or
+ lowerBound(bound, v, pos, false) and TZero() = exprSign(bound)
+ or
+ upperBound(bound, v, pos, _) and TPos() = exprSign(bound)
+ or
+ upperBound(bound, v, pos, false) and TZero() = exprSign(bound)
+ or
+ eqBound(bound, v, pos, true) and TZero() = exprSign(bound)
+ or
+ eqBound(bound, v, pos, false) and TZero() != exprSign(bound)
+}
+
+/**
+ * Holds if there is a bound that might restrict whether `v` has the sign `s`
+ * at `pos`.
+ */
+private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) {
+ s = TPos() and posBound(_, v, pos)
+ or
+ s = TNeg() and negBound(_, v, pos)
+ or
+ s = TZero() and zeroBound(_, v, pos)
+}
+
+pragma[noinline]
+private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) {
+ // SSA variable can have sign `result`
+ result = ssaDefSign(v) and
+ pos.hasReadOfVar(v) and
+ // there are guards at this position on `v` that might restrict it to be sign `result`.
+ // (So we need to check if they are satisfied)
+ hasGuard(v, pos, result)
+}
+
+pragma[noinline]
+private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) {
+ // SSA variable can have sign `result`
+ result = ssaDefSign(v) and
+ pos.hasReadOfVar(v) and
+ // there's no guard at this position on `v` that might restrict it to be sign `result`.
+ not hasGuard(v, pos, result)
+}
+
+/**
+ * Gets the sign of `v` at read position `pos`, when there's at least one guard
+ * on `v` at position `pos`. Each bound corresponding to a given sign must be met
+ * in order for `v` to be of that sign.
+ */
+private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
+ result = TPos() and
+ forex(Expr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
+ or
+ result = TNeg() and
+ forex(Expr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
+ or
+ result = TZero() and
+ forex(Expr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
+}
+
+/** Gets a possible sign for `v` at `pos`. */
+Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
+ result = unguardedSsaSign(v, pos)
+ or
+ result = guardedSsaSign(v, pos) and
+ result = guardedSsaSignOk(v, pos)
+}
+
+/** Gets a possible sign for `v`. */
+pragma[nomagic]
+Sign ssaDefSign(SsaVariable v) {
+ result = explicitSsaDefSign(v)
+ or
+ result = implicitSsaDefSign(v)
+ or
+ exists(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge |
+ v = phi and
+ edge.phiInput(phi, inp) and
+ result = ssaSign(inp, edge)
+ )
+}
+
+/** Gets a possible sign for `e`. */
+cached
+Sign exprSign(Expr e) {
+ result = certainExprSign(e)
+ or
+ not exists(certainExprSign(e)) and
+ (
+ unknownSign(e)
+ or
+ exists(SsaVariable v | getARead(v) = e | result = ssaVariableSign(v, e))
+ or
+ e =
+ any(VarAccess access |
+ not exists(SsaVariable v | getARead(v) = access) and
+ (
+ result = fieldSign(getField(access.(FieldAccess))) or
+ not access instanceof FieldAccess
+ )
+ )
+ or
+ result = specificSubExprSign(e)
+ )
+}
+
+/** Holds if `e` can be positive and cannot be negative. */
+predicate positive(Expr e) {
+ exprSign(e) = TPos() and
+ not exprSign(e) = TNeg()
+}
+
+/** Holds if `e` can be negative and cannot be positive. */
+predicate negative(Expr e) {
+ exprSign(e) = TNeg() and
+ not exprSign(e) = TPos()
+}
+
+/** Holds if `e` is strictly positive. */
+predicate strictlyPositive(Expr e) {
+ exprSign(e) = TPos() and
+ not exprSign(e) = TNeg() and
+ not exprSign(e) = TZero()
+}
+
+/** Holds if `e` is strictly negative. */
+predicate strictlyNegative(Expr e) {
+ exprSign(e) = TNeg() and
+ not exprSign(e) = TPos() and
+ not exprSign(e) = TZero()
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
new file mode 100644
index 00000000000..16784ccf0ec
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
@@ -0,0 +1,235 @@
+/**
+ * Provides Java-specific definitions for use in sign analysis.
+ */
+module Private {
+ import semmle.code.java.dataflow.RangeUtils as RU
+ private import semmle.code.java.dataflow.SSA as Ssa
+ private import semmle.code.java.controlflow.Guards as G
+ private import java as J
+ import Impl
+
+ class ConstantIntegerExpr = RU::ConstantIntegerExpr;
+
+ class Guard = G::Guard;
+
+ class SsaVariable = Ssa::SsaVariable;
+
+ class SsaPhiNode = Ssa::SsaPhiNode;
+
+ class VarAccess = J::VarAccess;
+
+ class FieldAccess = J::FieldAccess;
+
+ class CharacterLiteral = J::CharacterLiteral;
+
+ class IntegerLiteral = J::IntegerLiteral;
+
+ class LongLiteral = J::LongLiteral;
+
+ class CastExpr = J::CastExpr;
+
+ class Type = J::Type;
+
+ class Expr = J::Expr;
+
+ class ComparisonExpr = J::ComparisonExpr;
+
+ class NumericOrCharType = J::NumericOrCharType;
+
+ predicate ssaRead = RU::ssaRead/2;
+
+ predicate guardControlsSsaRead = RU::guardControlsSsaRead/3;
+}
+
+private module Impl {
+ private import java
+ private import semmle.code.java.dataflow.RangeUtils
+ private import semmle.code.java.dataflow.SSA
+ private import semmle.code.java.controlflow.Guards
+ private import semmle.code.java.Reflection
+ private import semmle.code.java.Collections
+ private import semmle.code.java.Maps
+ private import Sign
+ private import SignAnalysisCommon
+ private import SsaReadPositionCommon
+
+ float getNonIntegerValue(Expr e) {
+ result = e.(LongLiteral).getValue().toFloat() or
+ result = e.(FloatingPointLiteral).getValue().toFloat() or
+ result = e.(DoubleLiteral).getValue().toFloat()
+ }
+
+ string getCharValue(Expr e) { result = e.(CharacterLiteral).getValue() }
+
+ predicate containerSizeAccess(Expr e) {
+ e.(MethodAccess).getMethod() instanceof StringLengthMethod
+ or
+ e.(MethodAccess).getMethod() instanceof CollectionSizeMethod
+ or
+ e.(MethodAccess).getMethod() instanceof MapSizeMethod
+ }
+
+ predicate positiveExpression(Expr e) { none() }
+
+ predicate unknownIntegerAccess(Expr e) {
+ e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType
+ or
+ e instanceof MethodAccess and e.getType() instanceof NumericOrCharType
+ or
+ e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType
+ }
+
+ Sign explicitSsaDefSign(SsaVariable v) {
+ exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() |
+ result = exprSign(def.(VariableAssign).getSource())
+ or
+ exists(EnhancedForStmt for | def = for.getVariable())
+ or
+ result = exprSign(def.(PostIncExpr).getExpr()).inc()
+ or
+ result = exprSign(def.(PreIncExpr).getExpr()).inc()
+ or
+ result = exprSign(def.(PostDecExpr).getExpr()).dec()
+ or
+ result = exprSign(def.(PreDecExpr).getExpr()).dec()
+ or
+ exists(AssignOp a | a = def and result = exprSign(a))
+ )
+ }
+
+ Sign implicitSsaDefSign(SsaVariable v) {
+ result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable())
+ or
+ result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable())
+ or
+ exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p))
+ }
+
+ pragma[inline]
+ Sign ssaVariableSign(SsaVariable v, Expr e) {
+ result = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e))
+ or
+ not exists(SsaReadPositionBlock bb | getAnExpression(bb) = e) and
+ result = ssaDefSign(v)
+ }
+
+ /** Gets a possible sign for `f`. */
+ Sign fieldSign(Field f) {
+ result = exprSign(f.getAnAssignedValue())
+ or
+ exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
+ or
+ exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
+ or
+ exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
+ or
+ exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
+ or
+ exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a))
+ or
+ exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f)
+ or
+ if f.fromSource()
+ then not exists(f.getInitializer()) and result = TZero()
+ else
+ if f instanceof ArrayLengthField
+ then result != TNeg()
+ else
+ if f.hasName("MAX_VALUE")
+ then result = TPos()
+ else
+ if f.hasName("MIN_VALUE")
+ then result = TNeg()
+ else any()
+ }
+
+ Sign specificSubExprSign(Expr e) {
+ result = exprSign(e.(AssignExpr).getSource())
+ or
+ result = exprSign(e.(PlusExpr).getExpr())
+ or
+ result = exprSign(e.(PostIncExpr).getExpr())
+ or
+ result = exprSign(e.(PostDecExpr).getExpr())
+ or
+ result = exprSign(e.(PreIncExpr).getExpr()).inc()
+ or
+ result = exprSign(e.(PreDecExpr).getExpr()).dec()
+ or
+ result = exprSign(e.(MinusExpr).getExpr()).neg()
+ or
+ result = exprSign(e.(BitNotExpr).getExpr()).bitnot()
+ or
+ exists(DivExpr div |
+ div = e and
+ result = exprSign(div.getLeftOperand()) and
+ result != TZero()
+ |
+ div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or
+ div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0
+ )
+ or
+ exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) |
+ (e instanceof AssignAddExpr or e instanceof AddExpr) and
+ result = s1.add(s2)
+ or
+ (e instanceof AssignSubExpr or e instanceof SubExpr) and
+ result = s1.add(s2.neg())
+ or
+ (e instanceof AssignMulExpr or e instanceof MulExpr) and
+ result = s1.mul(s2)
+ or
+ (e instanceof AssignDivExpr or e instanceof DivExpr) and
+ result = s1.div(s2)
+ or
+ (e instanceof AssignRemExpr or e instanceof RemExpr) and
+ result = s1.rem(s2)
+ or
+ (e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and
+ result = s1.bitand(s2)
+ or
+ (e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and
+ result = s1.bitor(s2)
+ or
+ (e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and
+ result = s1.bitxor(s2)
+ or
+ (e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and
+ result = s1.lshift(s2)
+ or
+ (e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and
+ result = s1.rshift(s2)
+ or
+ (e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and
+ result = s1.urshift(s2)
+ )
+ or
+ result = exprSign(e.(ChooseExpr).getAResultExpr())
+ or
+ result = exprSign(e.(CastExpr).getExpr())
+ }
+
+ private Sign binaryOpLhsSign(Expr e) {
+ result = exprSign(e.(BinaryExpr).getLeftOperand()) or
+ result = exprSign(e.(AssignOp).getDest())
+ }
+
+ private Sign binaryOpRhsSign(Expr e) {
+ result = exprSign(e.(BinaryExpr).getRightOperand()) or
+ result = exprSign(e.(AssignOp).getRhs())
+ }
+
+ pragma[noinline]
+ private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) {
+ lhs = binaryOpLhsSign(e) and
+ rhs = binaryOpRhsSign(e)
+ }
+
+ Expr getARead(SsaVariable v) { result = v.getAUse() }
+
+ Field getField(FieldAccess fa) { result = fa.getField() }
+
+ Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() }
+
+ Guard getComparisonGuard(ComparisonExpr ce) { result = ce }
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
new file mode 100644
index 00000000000..558ecd1b88b
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll
@@ -0,0 +1,57 @@
+/**
+ * Provides classes for representing a position at which an SSA variable is read.
+ */
+
+private import SsaReadPositionSpecific
+
+private newtype TSsaReadPosition =
+ TSsaReadPositionBlock(BasicBlock bb) { bb = getAReadBasicBlock(_) } or
+ TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) {
+ exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock())
+ }
+
+/**
+ * A position at which an SSA variable is read. This includes both ordinary
+ * reads occurring in basic blocks and input to phi nodes occurring along an
+ * edge between two basic blocks.
+ */
+class SsaReadPosition extends TSsaReadPosition {
+ /** Holds if `v` is read at this position. */
+ abstract predicate hasReadOfVar(SsaVariable v);
+
+ /** Gets a textual representation of this SSA read position. */
+ abstract string toString();
+}
+
+/** A basic block in which an SSA variable is read. */
+class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock {
+ /** Gets the basic block corresponding to this position. */
+ BasicBlock getBlock() { this = TSsaReadPositionBlock(result) }
+
+ override predicate hasReadOfVar(SsaVariable v) { getBlock() = getAReadBasicBlock(v) }
+
+ override string toString() { result = "block" }
+}
+
+/**
+ * An edge between two basic blocks where the latter block has an SSA phi
+ * definition. The edge therefore has a read of an SSA variable serving as the
+ * input to the phi node.
+ */
+class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge {
+ /** Gets the source of the edge. */
+ BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) }
+
+ /** Gets the target of the edge. */
+ BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) }
+
+ override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) }
+
+ /** Holds if `inp` is an input to `phi` along this edge. */
+ predicate phiInput(SsaPhiNode phi, SsaVariable inp) {
+ phi.hasInputFromBlock(inp, getOrigBlock()) and
+ getPhiBlock() = phi.getBasicBlock()
+ }
+
+ override string toString() { result = "edge" }
+}
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
new file mode 100644
index 00000000000..dcfc3d69e32
--- /dev/null
+++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
@@ -0,0 +1,15 @@
+/**
+ * Provides Java-specific definitions for use in the `SsaReadPosition`.
+ */
+
+private import semmle.code.java.dataflow.SSA as Ssa
+private import semmle.code.java.controlflow.BasicBlocks as BB
+
+class SsaVariable = Ssa::SsaVariable;
+
+class SsaPhiNode = Ssa::SsaPhiNode;
+
+class BasicBlock = BB::BasicBlock;
+
+/** Gets a basic block in which SSA variable `v` is read. */
+BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() }
diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
index 1e3fe9acf27..a1dedfa95b1 100644
--- a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
+++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.expected
@@ -1,3 +1,3 @@
-| A.java:4:14:4:14 | x | negative strictlyNegative |
+| A.java:4:14:4:14 | x | strictlyNegative |
| A.java:6:9:6:9 | x | positive |
-| A.java:7:14:7:14 | y | positive strictlyPositive |
+| A.java:7:14:7:14 | y | strictlyPositive |
diff --git a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
index 7dd564dd77d..bc4fed33dc8 100644
--- a/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
+++ b/java/ql/test/library-tests/dataflow/sign-analysis/SignAnalysis.ql
@@ -1,17 +1,19 @@
import java
import semmle.code.java.dataflow.SignAnalysis
-string getASignString(Expr i) {
- positive(i) and
+string getASignString(Expr e) {
+ positive(e) and
+ not strictlyPositive(e) and
result = "positive"
or
- negative(i) and
+ negative(e) and
+ not strictlyNegative(e) and
result = "negative"
or
- strictlyPositive(i) and
+ strictlyPositive(e) and
result = "strictlyPositive"
or
- strictlyNegative(i) and
+ strictlyNegative(e) and
result = "strictlyNegative"
}
From 06dbec78f7fe8be7b82aa55ef9dbc1b42b1f52bc Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 15 Sep 2020 12:47:42 +0200
Subject: [PATCH 046/185] C#: Add `Guard::controlsBasicBlock()` and simplify
`Guard::isEquality()`
---
.../semmle/code/csharp/controlflow/Guards.qll | 125 ++++++++++--------
.../rangeanalysis/SignAnalysisSpecific.qll | 2 +-
.../dataflow/signanalysis/SignAnalysis.cs | 4 +-
.../signanalysis/SignAnalysis.expected | 3 +-
.../Nullness/EqualityCheck.expected | 4 +
5 files changed, 75 insertions(+), 63 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
index f81b3b09212..65a3bfe7213 100644
--- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
+++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll
@@ -33,29 +33,21 @@ class Guard extends Expr {
}
/**
- * Holds if guard is an equality test between `e1` and `e2`. If the test is
+ * Holds if basic block `bb` is guarded by this expression having value `v`.
+ */
+ predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) {
+ Internal::guardControls(this, bb, v)
+ }
+
+ /**
+ * Holds if this guard is an equality test between `e1` and `e2`. If the test is
* negated, that is `!=`, then `polarity` is false, otherwise `polarity` is
* true.
*/
predicate isEquality(Expr e1, Expr e2, boolean polarity) {
- exists(Expr exp1, Expr exp2 | equalityGuard(exp1, exp2, polarity) |
- e1 = exp1 and e2 = exp2
- or
- e2 = exp1 and e1 = exp2
- )
- }
-
- private predicate equalityGuard(Expr e1, Expr e2, boolean polarity) {
- exists(ComparisonTest eqtest |
- eqtest.getExpr() = this and
- eqtest.getAnArgument() = e1 and
- eqtest.getAnArgument() = e2 and
- not e1 = e2 and
- (
- polarity = true and eqtest.getComparisonKind().isEquality()
- or
- polarity = false and eqtest.getComparisonKind().isInequality()
- )
+ exists(BooleanValue v |
+ this = Internal::getAnEqualityCheck(e1, v, e2) and
+ polarity = v.getValue()
)
}
}
@@ -970,13 +962,14 @@ module Internal {
e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand())
}
- /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */
- private predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) {
- exists(ControlFlowElement cfe, ConditionalSuccessor s, AbstractValue v0, Guard g0 |
- cfe.controlsBlock(bb, s)
- |
- v0.branch(cfe, s, g0) and
- impliesSteps(g0, v0, g, v)
+ pragma[noinline]
+ private predicate assertionControlsNodeInSameBasicBlock0(
+ Guard g, AbstractValue v, BasicBlock bb, int i
+ ) {
+ exists(Assertion a, Guard g0, AbstractValue v0 |
+ asserts(a, g0, v0) and
+ impliesSteps(g0, v0, g, v) and
+ bb.getNode(i) = a.getAControlFlowNode()
)
}
@@ -984,17 +977,13 @@ module Internal {
* Holds if control flow node `cfn` only is reached when guard `g` evaluates to `v`,
* because of an assertion.
*/
- private predicate guardAssertionControlsNode(Guard g, ControlFlow::Node cfn, AbstractValue v) {
- exists(Assertion a, Guard g0, AbstractValue v0 |
- asserts(a, g0, v0) and
- impliesSteps(g0, v0, g, v)
- |
- a.strictlyDominates(cfn.getBasicBlock())
- or
- exists(BasicBlock bb, int i, int j | bb.getNode(i) = a.getAControlFlowNode() |
- bb.getNode(j) = cfn and
- j > i
- )
+ private predicate assertionControlsNodeInSameBasicBlock(
+ Guard g, ControlFlow::Node cfn, AbstractValue v
+ ) {
+ exists(BasicBlock bb, int i, int j |
+ assertionControlsNodeInSameBasicBlock0(g, v, bb, i) and
+ bb.getNode(j) = cfn and
+ j > i
)
}
@@ -1004,7 +993,7 @@ module Internal {
*/
private predicate guardAssertionControlsElement(Guard g, ControlFlowElement cfe, AbstractValue v) {
forex(ControlFlow::Node cfn | cfn = cfe.getAControlFlowNode() |
- guardAssertionControlsNode(g, cfn, v)
+ assertionControlsNodeInSameBasicBlock(g, cfn, v)
)
}
@@ -1318,24 +1307,6 @@ module Internal {
)
}
- /**
- * Gets an expression that tests whether expression `e1` is equal to
- * expression `e2`.
- *
- * If the returned expression has abstract value `v`, then expression `e1` is
- * guaranteed to be equal to `e2`, and if the returned expression has abstract
- * value `v.getDualValue()`, then this expression is guaranteed to be
- * non-equal to `e`.
- *
- * For example, if the expression `x != ""` evaluates to `false` then the
- * expression `x` is guaranteed to be equal to `""`.
- */
- Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) {
- result = getABooleanEqualityCheck(e1, v, e2)
- or
- result = getAMatchingEqualityCheck(e1, v, e2)
- }
-
private Expr getAnEqualityCheckVal(Expr e, AbstractValue v, AbstractValue vExpr) {
result = getAnEqualityCheck(e, v, vExpr.getAnExpr())
}
@@ -1491,6 +1462,29 @@ module Internal {
not e = any(LocalVariableDeclStmt s).getAVariableDeclExpr()
}
+ /**
+ * Gets an expression that tests whether expression `e1` is equal to
+ * expression `e2`.
+ *
+ * If the returned expression has abstract value `v`, then expression `e1` is
+ * guaranteed to be equal to `e2`, and if the returned expression has abstract
+ * value `v.getDualValue()`, then this expression is guaranteed to be
+ * non-equal to `e`.
+ *
+ * For example, if the expression `x != ""` evaluates to `false` then the
+ * expression `x` is guaranteed to be equal to `""`.
+ */
+ cached
+ Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) {
+ result = getABooleanEqualityCheck(e1, v, e2)
+ or
+ result = getABooleanEqualityCheck(e2, v, e1)
+ or
+ result = getAMatchingEqualityCheck(e1, v, e2)
+ or
+ result = getAMatchingEqualityCheck(e2, v, e1)
+ }
+
cached
predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) {
exists(Callable callable, Parameter p |
@@ -1776,7 +1770,7 @@ module Internal {
exists(Guard g | e = getAChildExprStar(g) |
guardControls(g, bb, _)
or
- guardAssertionControlsNode(g, bb.getANode(), _)
+ assertionControlsNodeInSameBasicBlock(g, bb.getANode(), _)
)
}
}
@@ -1785,6 +1779,21 @@ module Internal {
private module Cached {
private import semmle.code.csharp.Caching
+ /** Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. */
+ cached
+ predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) {
+ exists(AbstractValue v0, Guard g0 | impliesSteps(g0, v0, g, v) |
+ exists(ControlFlowElement cfe, ConditionalSuccessor s |
+ v0.branch(cfe, s, g0) and cfe.controlsBlock(bb, s)
+ )
+ or
+ exists(Assertion a |
+ asserts(a, g0, v0) and
+ a.strictlyDominates(bb)
+ )
+ )
+ }
+
pragma[noinline]
private predicate isGuardedByNode0(
ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub,
@@ -1840,7 +1849,7 @@ module Internal {
) {
isGuardedByNode0(guarded, _, g, sub, v)
or
- guardAssertionControlsNode(g, guarded, v) and
+ assertionControlsNodeInSameBasicBlock(g, guarded, v) and
exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded.getElement()))
}
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
index 75adec96909..a47434eed0d 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
@@ -243,7 +243,7 @@ private module Impl {
*/
predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
exists(BooleanValue b | b.getValue() = testIsTrue |
- guard.controlsNode(controlled.(SsaReadPositionBlock).getBlock().getANode(), _, b)
+ guard.controlsBasicBlock(controlled.(SsaReadPositionBlock).getBlock(), b)
)
}
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
index 13a94a18aae..5a2df6428ff 100644
--- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.cs
@@ -155,7 +155,7 @@ class SignAnalysis
if (y is -1)
{
- return y; // strictly negative [MISSING]
+ return y; // strictly negative
}
if (x < y)
@@ -289,7 +289,7 @@ class SignAnalysis
void Assert(int i, bool b)
{
Debug.Assert(i > 0);
- System.Console.WriteLine(i); // strictly positive
+ System.Console.WriteLine(i); // strictly positive [MISSING]
if (b)
System.Console.WriteLine(i); // strictly positive
diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
index 91d429a45ef..31220df1a3c 100644
--- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
+++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.expected
@@ -106,6 +106,7 @@
| SignAnalysis.cs:153:20:153:20 | access to parameter y | strictlyPositive |
| SignAnalysis.cs:156:18:156:19 | -... | strictlyNegative |
| SignAnalysis.cs:156:19:156:19 | 1 | strictlyPositive |
+| SignAnalysis.cs:158:20:158:20 | access to parameter y | strictlyNegative |
| SignAnalysis.cs:161:13:161:13 | access to parameter x | positive |
| SignAnalysis.cs:163:20:163:20 | access to parameter y | strictlyPositive |
| SignAnalysis.cs:166:22:166:22 | 1 | strictlyPositive |
@@ -154,8 +155,6 @@
| SignAnalysis.cs:278:42:278:42 | access to parameter k | positive |
| SignAnalysis.cs:280:21:280:21 | access to parameter k | positive |
| SignAnalysis.cs:280:26:280:26 | 5 | strictlyPositive |
-| SignAnalysis.cs:291:22:291:22 | access to parameter i | strictlyPositive |
-| SignAnalysis.cs:292:34:292:34 | access to parameter i | strictlyPositive |
| SignAnalysis.cs:295:38:295:38 | access to parameter i | strictlyPositive |
| SignAnalysis.cs:300:27:300:28 | -... | strictlyNegative |
| SignAnalysis.cs:300:28:300:28 | 1 | strictlyPositive |
diff --git a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected
index 5f739e93c2b..dc70346eea5 100644
--- a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected
+++ b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected
@@ -191,8 +191,11 @@
| E.cs:85:18:85:29 | ... != ... | false | E.cs:85:18:85:21 | access to parameter vals | E.cs:85:26:85:29 | null |
| E.cs:85:18:85:29 | ... != ... | false | E.cs:85:26:85:29 | null | E.cs:85:18:85:21 | access to parameter vals |
| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:92:18:92:27 | access to constant MY_CONST_A |
+| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:92:18:92:27 | access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard |
| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:97:18:97:27 | access to constant MY_CONST_B |
+| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:97:18:97:27 | access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard |
| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:95:18:95:27 | access to constant MY_CONST_C |
+| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:95:18:95:27 | access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard |
| E.cs:126:21:126:29 | ... == ... | true | E.cs:126:21:126:24 | access to local variable step | E.cs:126:29:126:29 | 0 |
| E.cs:126:21:126:29 | ... == ... | true | E.cs:126:29:126:29 | 0 | E.cs:126:21:126:24 | access to local variable step |
| E.cs:153:13:153:24 | ... != ... | false | E.cs:153:13:153:16 | access to local variable obj2 | E.cs:153:21:153:24 | null |
@@ -216,6 +219,7 @@
| E.cs:293:13:293:24 | ... == ... | true | E.cs:293:15:293:19 | call to method M2 | E.cs:293:24:293:24 | (...) ... |
| E.cs:293:13:293:24 | ... == ... | true | E.cs:293:24:293:24 | (...) ... | E.cs:293:15:293:19 | call to method M2 |
| E.cs:321:13:321:30 | ... is ... | true | E.cs:321:14:321:21 | ... ?? ... | E.cs:321:27:321:30 | null |
+| E.cs:321:13:321:30 | ... is ... | true | E.cs:321:27:321:30 | null | E.cs:321:14:321:21 | ... ?? ... |
| E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:13:355:13 | access to local variable x | E.cs:355:18:355:21 | null |
| E.cs:355:13:355:21 | dynamic call to operator != | false | E.cs:355:18:355:21 | null | E.cs:355:13:355:13 | access to local variable x |
| E.cs:362:13:362:29 | ... != ... | false | E.cs:362:13:362:13 | access to local variable x | E.cs:362:18:362:29 | (...) ... |
From 73d2d9b1f86fafee048949059ef5d74b5400b9c7 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 21 Sep 2020 17:32:22 +0200
Subject: [PATCH 047/185] Python: Make constructor calls post-update nodes
---
.../dataflow/internal/DataFlowPrivate.qll | 101 +++++++----
.../dataflow/internal/DataFlowPublic.qll | 6 +-
.../coverage/classesCallGraph.expected | 162 +-----------------
.../dataflow/coverage/dataflow.expected | 12 ++
.../dataflow/coverage/datamodel.py | 2 +-
.../dataflow/fieldflow/allLocalFlow.expected | 4 -
.../dataflow/fieldflow/dataflow.expected | 16 +-
.../fieldflow/dataflowExplore.expected | 8 +-
.../dataflow/fieldflow/globalStep.expected | 121 ++++++-------
.../dataflow/fieldflow/localFlow.expected | 2 -
.../dataflow/fieldflow/postupdates.expected | 20 +--
11 files changed, 168 insertions(+), 286 deletions(-)
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
index b598c25a5e3..22d483bea7a 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll
@@ -16,18 +16,42 @@ class DataFlowCfgNode extends ControlFlowNode {
DataFlowCfgNode() { isExpressionNode(this) }
}
-/** A data flow node which should have an associated post-update node. */
-abstract class PreUpdateNode extends Node {
+/** A data flow node for which we should synthesise an associated pre-update node. */
+abstract class NeedsSyntheticPreUpdateNode extends Node {
+ abstract string label();
+}
+
+class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
+ NeedsSyntheticPreUpdateNode post;
+
+ SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(post) }
+
+ /** Gets the node for which this is a synthetic pre-update node. */
+ Node getPostUpdateNode() { result = post }
+
+ override string toString() { result = "[pre " + post.label() + "] " + post.toString() }
+
+ override Scope getScope() { result = post.getScope() }
+
+ override Location getLocation() { result = post.getLocation() }
+}
+
+/** A data flow node for which we should synthesise an associated post-update node. */
+abstract class NeedsSyntheticPostUpdateNode extends Node {
abstract string label();
}
/** An argument might have its value changed as a result of a call. */
-class ArgumentPreUpdateNode extends PreUpdateNode, ExplicitArgumentNode {
+class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode {
+ // Certain arguments, such as implicit self arguments are already post-update nodes
+ // and should not have an extra node synthesised.
+ ArgumentPreUpdateNode() { this.isNotPostUpdate() }
+
override string label() { result = "arg" }
}
/** An object might have its value changed after a store. */
-class StorePreUpdateNode extends PreUpdateNode, CfgNode {
+class StorePreUpdateNode extends NeedsSyntheticPostUpdateNode, CfgNode {
StorePreUpdateNode() {
exists(Attribute a |
node = a.getObject().getAFlowNode() and
@@ -39,7 +63,7 @@ class StorePreUpdateNode extends PreUpdateNode, CfgNode {
}
/** A node marking the state change of an object after a read. */
-class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
+class ReadPreUpdateNode extends NeedsSyntheticPostUpdateNode, CfgNode {
ReadPreUpdateNode() {
exists(Attribute a |
node = a.getObject().getAFlowNode() and
@@ -58,16 +82,21 @@ class ReadPreUpdateNode extends PreUpdateNode, CfgNode {
* (which might have mutated the argument), or the qualifier of a field after
* an update to the field.
*
- * Nodes corresponding to AST elements, for example `ExprNode`, usually refer
- * to the value before the update.
+ * Nodes corresponding to AST elements, for example `ExprNode`s, usually refer
+ * to the value before the update with the exception of `ObjectCreationNode`s,
+ * which represents the value after the constructor has run.
*/
-class PostUpdateNode extends Node, TPostUpdateNode {
- PreUpdateNode pre;
-
- PostUpdateNode() { this = TPostUpdateNode(pre) }
-
+abstract class PostUpdateNode extends Node {
/** Gets the node before the state update. */
- Node getPreUpdateNode() { result = pre }
+ abstract Node getPreUpdateNode();
+}
+
+class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode {
+ NeedsSyntheticPostUpdateNode pre;
+
+ SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(pre) }
+
+ override Node getPreUpdateNode() { result = pre }
override string toString() { result = "[post " + pre.label() + "] " + pre.toString() }
@@ -76,6 +105,14 @@ class PostUpdateNode extends Node, TPostUpdateNode {
override Location getLocation() { result = pre.getLocation() }
}
+class ObjectCreationNode extends PostUpdateNode, NeedsSyntheticPreUpdateNode, CfgNode {
+ ObjectCreationNode() { node.(CallNode) = any(ClassValue c).getACall() }
+
+ override Node getPreUpdateNode() { result.(SyntheticPreUpdateNode).getPostUpdateNode() = this }
+
+ override string label() { result = "objCreate" }
+}
+
class DataFlowExpr = Expr;
/**
@@ -199,7 +236,7 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract DataFlowCallable getCallable();
/** Get the specified argument to this call. */
- abstract ControlFlowNode getArg(int n);
+ abstract Node getArg(int n);
/** Get the control flow node representing this call. */
abstract ControlFlowNode getNode();
@@ -220,7 +257,7 @@ class CallNodeCall extends DataFlowCall, TCallNode {
override string toString() { result = call.toString() }
- override ControlFlowNode getArg(int n) { result = call.getArg(n) }
+ override Node getArg(int n) { result = TCfgNode(call.getArg(n)) }
override ControlFlowNode getNode() { result = call }
@@ -241,10 +278,10 @@ class ClassCall extends DataFlowCall, TClassCall {
override string toString() { result = call.toString() }
- override ControlFlowNode getArg(int n) {
- result = call.getArg(n - 1)
+ override Node getArg(int n) {
+ n > 0 and result = TCfgNode(call.getArg(n - 1))
or
- n = 0 and result = call
+ n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call))
}
override ControlFlowNode getNode() { result = call }
@@ -267,7 +304,7 @@ class SpecialCall extends DataFlowCall, TSpecialCall {
override string toString() { result = special.toString() }
- override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) }
+ override Node getArg(int n) { result = TCfgNode(special.(SpecialMethod::Potential).getArg(n)) }
override ControlFlowNode getNode() { result = special }
@@ -279,23 +316,23 @@ class SpecialCall extends DataFlowCall, TSpecialCall {
}
/** A data flow node that represents a call argument. */
-abstract class ArgumentNode extends CfgNode {
- /** Holds if this argument occurs at the given position in the given call. */
- abstract predicate argumentOf(DataFlowCall call, int pos);
-
- /** Gets the call in which this node is an argument. */
- abstract DataFlowCall getCall();
-}
-
-/** A data flow node that represents a call argument. */
-class ExplicitArgumentNode extends ArgumentNode {
- ExplicitArgumentNode() { exists(DataFlowCall call, int pos | node = call.getArg(pos)) }
+class ArgumentNode extends Node {
+ ArgumentNode() { this = any(DataFlowCall c).getArg(_) }
/** Holds if this argument occurs at the given position in the given call. */
- override predicate argumentOf(DataFlowCall call, int pos) { node = call.getArg(pos) }
+ predicate argumentOf(DataFlowCall call, int pos) { this = call.getArg(pos) }
/** Gets the call in which this node is an argument. */
- final override DataFlowCall getCall() { this.argumentOf(result, _) }
+ final DataFlowCall getCall() { this.argumentOf(result, _) }
+
+ predicate isNotPostUpdate() {
+ // Avoid argument 0 of class calls as those have non-synthetic post-update nodes.
+ exists(CallNodeCall c | this = c.getArg(_))
+ or
+ exists(ClassCall c, int n | n > 0 | this = c.getArg(n))
+ or
+ exists(SpecialCall c | this = c.getArg(_))
+ }
}
/** Gets a viable run-time target for the call `call`. */
diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
index e9e143ca7ad..038721d6f52 100644
--- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
+++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll
@@ -23,8 +23,10 @@ newtype TNode =
TEssaNode(EssaVariable var) or
/** A node corresponding to a control flow node. */
TCfgNode(DataFlowCfgNode node) or
- /** A node representing the value of an object after a state change */
- TPostUpdateNode(PreUpdateNode pre)
+ /** A synthetic node representing the value of an object before a state change */
+ TSyntheticPreUpdateNode(NeedsSyntheticPreUpdateNode post) or
+ /** A synthetic node representing the value of an object after a state change */
+ TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre)
/**
* An element, viewed as a node in a data flow graph. Either an SSA variable
diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
index a34e34424a7..cf688c17a49 100644
--- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
+++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected
@@ -1,200 +1,40 @@
-| classes.py:41:16:41:22 | ControlFlowNode for super() | classes.py:41:16:41:22 | ControlFlowNode for super() |
| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() |
-| classes.py:45:16:45:25 | ControlFlowNode for With_new() | classes.py:45:16:45:25 | ControlFlowNode for With_new() |
-| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self |
-| classes.py:58:17:58:27 | ControlFlowNode for With_init() | classes.py:58:17:58:27 | ControlFlowNode for With_init() |
-| classes.py:71:16:71:25 | ControlFlowNode for With_del() | classes.py:71:16:71:25 | ControlFlowNode for With_del() |
-| classes.py:86:17:86:27 | ControlFlowNode for With_repr() | classes.py:86:17:86:27 | ControlFlowNode for With_repr() |
-| classes.py:101:16:101:25 | ControlFlowNode for With_str() | classes.py:101:16:101:25 | ControlFlowNode for With_str() |
-| classes.py:102:5:102:17 | ControlFlowNode for str() | classes.py:102:5:102:17 | ControlFlowNode for str() |
-| classes.py:116:18:116:29 | ControlFlowNode for With_bytes() | classes.py:116:18:116:29 | ControlFlowNode for With_bytes() |
-| classes.py:117:5:117:21 | ControlFlowNode for bytes() | classes.py:117:5:117:21 | ControlFlowNode for bytes() |
-| classes.py:132:19:132:31 | ControlFlowNode for With_format() | classes.py:132:19:132:31 | ControlFlowNode for With_format() |
-| classes.py:138:19:138:31 | ControlFlowNode for With_format() | classes.py:138:19:138:31 | ControlFlowNode for With_format() |
-| classes.py:143:19:143:31 | ControlFlowNode for With_format() | classes.py:143:19:143:31 | ControlFlowNode for With_format() |
-| classes.py:159:15:159:23 | ControlFlowNode for With_lt() | classes.py:159:15:159:23 | ControlFlowNode for With_lt() |
-| classes.py:176:15:176:23 | ControlFlowNode for With_le() | classes.py:176:15:176:23 | ControlFlowNode for With_le() |
-| classes.py:193:15:193:23 | ControlFlowNode for With_eq() | classes.py:193:15:193:23 | ControlFlowNode for With_eq() |
-| classes.py:209:15:209:23 | ControlFlowNode for With_ne() | classes.py:209:15:209:23 | ControlFlowNode for With_ne() |
-| classes.py:225:15:225:23 | ControlFlowNode for With_gt() | classes.py:225:15:225:23 | ControlFlowNode for With_gt() |
-| classes.py:242:15:242:23 | ControlFlowNode for With_ge() | classes.py:242:15:242:23 | ControlFlowNode for With_ge() |
-| classes.py:258:17:258:27 | ControlFlowNode for With_hash() | classes.py:258:17:258:27 | ControlFlowNode for With_hash() |
-| classes.py:263:17:263:27 | ControlFlowNode for With_hash() | classes.py:263:17:263:27 | ControlFlowNode for With_hash() |
+| classes.py:58:17:58:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self |
| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() |
-| classes.py:268:17:268:27 | ControlFlowNode for With_hash() | classes.py:268:17:268:27 | ControlFlowNode for With_hash() |
| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() |
-| classes.py:273:17:273:27 | ControlFlowNode for With_hash() | classes.py:273:17:273:27 | ControlFlowNode for With_hash() |
| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() |
-| classes.py:288:17:288:27 | ControlFlowNode for With_bool() | classes.py:288:17:288:27 | ControlFlowNode for With_bool() |
-| classes.py:289:5:289:19 | ControlFlowNode for bool() | classes.py:289:5:289:19 | ControlFlowNode for bool() |
-| classes.py:293:17:293:27 | ControlFlowNode for With_bool() | classes.py:293:17:293:27 | ControlFlowNode for With_bool() |
-| classes.py:311:20:311:33 | ControlFlowNode for With_getattr() | classes.py:311:20:311:33 | ControlFlowNode for With_getattr() |
-| classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() | classes.py:327:25:327:43 | ControlFlowNode for With_getattribute() |
-| classes.py:343:20:343:33 | ControlFlowNode for With_setattr() | classes.py:343:20:343:33 | ControlFlowNode for With_setattr() |
-| classes.py:359:20:359:33 | ControlFlowNode for With_delattr() | classes.py:359:20:359:33 | ControlFlowNode for With_delattr() |
-| classes.py:374:16:374:25 | ControlFlowNode for With_dir() | classes.py:374:16:374:25 | ControlFlowNode for With_dir() |
-| classes.py:399:16:399:25 | ControlFlowNode for With_get() | classes.py:399:16:399:25 | ControlFlowNode for With_get() |
-| classes.py:401:12:401:17 | ControlFlowNode for arg3() | classes.py:401:12:401:17 | ControlFlowNode for arg3() |
-| classes.py:417:16:417:25 | ControlFlowNode for With_set() | classes.py:417:16:417:25 | ControlFlowNode for With_set() |
-| classes.py:419:12:419:18 | ControlFlowNode for Owner() | classes.py:419:12:419:18 | ControlFlowNode for Owner() |
-| classes.py:435:19:435:31 | ControlFlowNode for With_delete() | classes.py:435:19:435:31 | ControlFlowNode for With_delete() |
-| classes.py:437:12:437:18 | ControlFlowNode for Owner() | classes.py:437:12:437:18 | ControlFlowNode for Owner() |
-| classes.py:453:21:453:35 | ControlFlowNode for With_set_name() | classes.py:453:21:453:35 | ControlFlowNode for With_set_name() |
-| classes.py:454:5:454:53 | ControlFlowNode for type() | classes.py:454:5:454:53 | ControlFlowNode for type() |
| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() |
-| classes.py:473:5:473:47 | ControlFlowNode for type() | classes.py:473:5:473:47 | ControlFlowNode for type() |
-| classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() | classes.py:513:26:513:45 | ControlFlowNode for With_instancecheck() |
-| classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() | classes.py:530:26:530:45 | ControlFlowNode for With_subclasscheck() |
-| classes.py:548:26:548:51 | ControlFlowNode for Subscript() | classes.py:548:26:548:51 | ControlFlowNode for Subscript() |
-| classes.py:561:17:561:27 | ControlFlowNode for With_call() | classes.py:561:17:561:27 | ControlFlowNode for With_call() |
-| classes.py:577:16:577:25 | ControlFlowNode for With_len() | classes.py:577:16:577:25 | ControlFlowNode for With_len() |
-| classes.py:582:16:582:25 | ControlFlowNode for With_len() | classes.py:582:16:582:25 | ControlFlowNode for With_len() |
-| classes.py:583:5:583:18 | ControlFlowNode for bool() | classes.py:583:5:583:18 | ControlFlowNode for bool() |
-| classes.py:587:16:587:25 | ControlFlowNode for With_len() | classes.py:587:16:587:25 | ControlFlowNode for With_len() |
-| classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() | classes.py:604:24:604:41 | ControlFlowNode for With_length_hint() |
-| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:612:21:612:24 | SSA variable self |
-| classes.py:620:20:620:33 | ControlFlowNode for With_getitem() | classes.py:620:20:620:33 | ControlFlowNode for With_getitem() |
| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self |
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key |
-| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:629:21:629:24 | SSA variable self |
-| classes.py:637:20:637:33 | ControlFlowNode for With_setitem() | classes.py:637:20:637:33 | ControlFlowNode for With_setitem() |
| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self |
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key |
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value |
-| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:647:21:647:24 | SSA variable self |
-| classes.py:654:20:654:33 | ControlFlowNode for With_delitem() | classes.py:654:20:654:33 | ControlFlowNode for With_delitem() |
| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self |
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key |
-| classes.py:671:20:671:33 | ControlFlowNode for With_missing() | classes.py:671:20:671:33 | ControlFlowNode for With_missing() |
| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() |
-| classes.py:687:17:687:27 | ControlFlowNode for With_iter() | classes.py:687:17:687:27 | ControlFlowNode for With_iter() |
-| classes.py:702:21:702:35 | ControlFlowNode for With_reversed() | classes.py:702:21:702:35 | ControlFlowNode for With_reversed() |
-| classes.py:703:5:703:27 | ControlFlowNode for reversed() | classes.py:703:5:703:27 | ControlFlowNode for reversed() |
-| classes.py:718:21:718:35 | ControlFlowNode for With_contains() | classes.py:718:21:718:35 | ControlFlowNode for With_contains() |
-| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:17:727:20 | SSA variable self |
-| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:727:23:727:27 | SSA variable other |
-| classes.py:735:16:735:25 | ControlFlowNode for With_add() | classes.py:735:16:735:25 | ControlFlowNode for With_add() |
| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self |
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other |
-| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:17:744:20 | SSA variable self |
-| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:744:23:744:27 | SSA variable other |
-| classes.py:752:16:752:25 | ControlFlowNode for With_sub() | classes.py:752:16:752:25 | ControlFlowNode for With_sub() |
| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self |
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other |
-| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:17:761:20 | SSA variable self |
-| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:761:23:761:27 | SSA variable other |
-| classes.py:769:16:769:25 | ControlFlowNode for With_mul() | classes.py:769:16:769:25 | ControlFlowNode for With_mul() |
| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self |
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other |
-| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:20:778:23 | SSA variable self |
-| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:778:26:778:30 | SSA variable other |
-| classes.py:786:19:786:31 | ControlFlowNode for With_matmul() | classes.py:786:19:786:31 | ControlFlowNode for With_matmul() |
| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self |
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other |
-| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:21:795:24 | SSA variable self |
-| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:795:27:795:31 | SSA variable other |
-| classes.py:803:20:803:33 | ControlFlowNode for With_truediv() | classes.py:803:20:803:33 | ControlFlowNode for With_truediv() |
| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self |
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other |
-| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:22:812:25 | SSA variable self |
-| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:812:28:812:32 | SSA variable other |
-| classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() | classes.py:820:21:820:35 | ControlFlowNode for With_floordiv() |
| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self |
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other |
-| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:17:829:20 | SSA variable self |
-| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:829:23:829:27 | SSA variable other |
-| classes.py:837:16:837:25 | ControlFlowNode for With_mod() | classes.py:837:16:837:25 | ControlFlowNode for With_mod() |
| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self |
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other |
-| classes.py:854:19:854:31 | ControlFlowNode for With_divmod() | classes.py:854:19:854:31 | ControlFlowNode for With_divmod() |
-| classes.py:871:16:871:25 | ControlFlowNode for With_pow() | classes.py:871:16:871:25 | ControlFlowNode for With_pow() |
-| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:17:863:20 | SSA variable self |
-| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:863:23:863:27 | SSA variable other |
-| classes.py:877:16:877:25 | ControlFlowNode for With_pow() | classes.py:877:16:877:25 | ControlFlowNode for With_pow() |
| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self |
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other |
-| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:20:886:23 | SSA variable self |
-| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:886:26:886:30 | SSA variable other |
-| classes.py:894:19:894:31 | ControlFlowNode for With_lshift() | classes.py:894:19:894:31 | ControlFlowNode for With_lshift() |
| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self |
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other |
-| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:20:903:23 | SSA variable self |
-| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:903:26:903:30 | SSA variable other |
-| classes.py:911:19:911:31 | ControlFlowNode for With_rshift() | classes.py:911:19:911:31 | ControlFlowNode for With_rshift() |
| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self |
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other |
-| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:17:920:20 | SSA variable self |
-| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:920:23:920:27 | SSA variable other |
-| classes.py:928:16:928:25 | ControlFlowNode for With_and() | classes.py:928:16:928:25 | ControlFlowNode for With_and() |
| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self |
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other |
-| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:17:937:20 | SSA variable self |
-| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:937:23:937:27 | SSA variable other |
-| classes.py:945:16:945:25 | ControlFlowNode for With_xor() | classes.py:945:16:945:25 | ControlFlowNode for With_xor() |
| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self |
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other |
-| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:16:954:19 | SSA variable self |
-| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:954:22:954:26 | SSA variable other |
-| classes.py:962:15:962:23 | ControlFlowNode for With_or() | classes.py:962:15:962:23 | ControlFlowNode for With_or() |
| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self |
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other |
-| classes.py:979:17:979:27 | ControlFlowNode for With_radd() | classes.py:979:17:979:27 | ControlFlowNode for With_radd() |
-| classes.py:996:17:996:27 | ControlFlowNode for With_rsub() | classes.py:996:17:996:27 | ControlFlowNode for With_rsub() |
-| classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() | classes.py:1013:17:1013:27 | ControlFlowNode for With_rmul() |
-| classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() | classes.py:1030:20:1030:33 | ControlFlowNode for With_rmatmul() |
-| classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() | classes.py:1047:21:1047:35 | ControlFlowNode for With_rtruediv() |
-| classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() | classes.py:1064:22:1064:37 | ControlFlowNode for With_rfloordiv() |
-| classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() | classes.py:1081:17:1081:27 | ControlFlowNode for With_rmod() |
-| classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() | classes.py:1098:20:1098:33 | ControlFlowNode for With_rdivmod() |
-| classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() | classes.py:1115:17:1115:27 | ControlFlowNode for With_rpow() |
-| classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() | classes.py:1121:17:1121:27 | ControlFlowNode for With_rpow() |
-| classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() | classes.py:1138:20:1138:33 | ControlFlowNode for With_rlshift() |
-| classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() | classes.py:1155:20:1155:33 | ControlFlowNode for With_rrshift() |
-| classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() | classes.py:1172:17:1172:27 | ControlFlowNode for With_rand() |
-| classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() | classes.py:1189:17:1189:27 | ControlFlowNode for With_rxor() |
-| classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() | classes.py:1206:16:1206:25 | ControlFlowNode for With_ror() |
-| classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() | classes.py:1223:17:1223:27 | ControlFlowNode for With_iadd() |
-| classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() | classes.py:1240:17:1240:27 | ControlFlowNode for With_isub() |
-| classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() | classes.py:1257:17:1257:27 | ControlFlowNode for With_imul() |
-| classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() | classes.py:1274:20:1274:33 | ControlFlowNode for With_imatmul() |
-| classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() | classes.py:1291:21:1291:35 | ControlFlowNode for With_itruediv() |
-| classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() | classes.py:1308:22:1308:37 | ControlFlowNode for With_ifloordiv() |
-| classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() | classes.py:1325:17:1325:27 | ControlFlowNode for With_imod() |
-| classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() | classes.py:1342:17:1342:27 | ControlFlowNode for With_ipow() |
-| classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() | classes.py:1359:20:1359:33 | ControlFlowNode for With_ilshift() |
-| classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() | classes.py:1376:20:1376:33 | ControlFlowNode for With_irshift() |
-| classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() | classes.py:1393:17:1393:27 | ControlFlowNode for With_iand() |
-| classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() | classes.py:1410:17:1410:27 | ControlFlowNode for With_ixor() |
-| classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() | classes.py:1427:16:1427:25 | ControlFlowNode for With_ior() |
-| classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() | classes.py:1443:16:1443:25 | ControlFlowNode for With_neg() |
-| classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() | classes.py:1458:16:1458:25 | ControlFlowNode for With_pos() |
-| classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() | classes.py:1473:16:1473:25 | ControlFlowNode for With_abs() |
-| classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() | classes.py:1488:19:1488:31 | ControlFlowNode for With_invert() |
-| classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() | classes.py:1503:20:1503:33 | ControlFlowNode for With_complex() |
-| classes.py:1504:5:1504:25 | ControlFlowNode for complex() | classes.py:1504:5:1504:25 | ControlFlowNode for complex() |
-| classes.py:1518:16:1518:25 | ControlFlowNode for With_int() | classes.py:1518:16:1518:25 | ControlFlowNode for With_int() |
-| classes.py:1519:5:1519:17 | ControlFlowNode for int() | classes.py:1519:5:1519:17 | ControlFlowNode for int() |
-| classes.py:1533:18:1533:29 | ControlFlowNode for With_float() | classes.py:1533:18:1533:29 | ControlFlowNode for With_float() |
-| classes.py:1534:5:1534:21 | ControlFlowNode for float() | classes.py:1534:5:1534:21 | ControlFlowNode for float() |
-| classes.py:1549:18:1549:29 | ControlFlowNode for With_index() | classes.py:1549:18:1549:29 | ControlFlowNode for With_index() |
-| classes.py:1554:18:1554:29 | ControlFlowNode for With_index() | classes.py:1554:18:1554:29 | ControlFlowNode for With_index() |
-| classes.py:1559:18:1559:29 | ControlFlowNode for With_index() | classes.py:1559:18:1559:29 | ControlFlowNode for With_index() |
-| classes.py:1564:18:1564:29 | ControlFlowNode for With_index() | classes.py:1564:18:1564:29 | ControlFlowNode for With_index() |
-| classes.py:1569:18:1569:29 | ControlFlowNode for With_index() | classes.py:1569:18:1569:29 | ControlFlowNode for With_index() |
-| classes.py:1574:18:1574:29 | ControlFlowNode for With_index() | classes.py:1574:18:1574:29 | ControlFlowNode for With_index() |
-| classes.py:1575:5:1575:19 | ControlFlowNode for int() | classes.py:1575:5:1575:19 | ControlFlowNode for int() |
-| classes.py:1579:18:1579:29 | ControlFlowNode for With_index() | classes.py:1579:18:1579:29 | ControlFlowNode for With_index() |
-| classes.py:1580:5:1580:21 | ControlFlowNode for float() | classes.py:1580:5:1580:21 | ControlFlowNode for float() |
-| classes.py:1584:18:1584:29 | ControlFlowNode for With_index() | classes.py:1584:18:1584:29 | ControlFlowNode for With_index() |
-| classes.py:1585:5:1585:23 | ControlFlowNode for complex() | classes.py:1585:5:1585:23 | ControlFlowNode for complex() |
-| classes.py:1599:18:1599:29 | ControlFlowNode for With_round() | classes.py:1599:18:1599:29 | ControlFlowNode for With_round() |
-| classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() | classes.py:1614:18:1614:29 | ControlFlowNode for With_trunc() |
-| classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() | classes.py:1630:18:1630:29 | ControlFlowNode for With_floor() |
-| classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() | classes.py:1646:17:1646:27 | ControlFlowNode for With_ceil() |
-| classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() | classes.py:1665:10:1665:21 | ControlFlowNode for With_enter() |
-| classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() | classes.py:1686:10:1686:20 | ControlFlowNode for With_exit() |
-| classes.py:1703:18:1703:29 | ControlFlowNode for With_await() | classes.py:1703:18:1703:29 | ControlFlowNode for With_await() |
-| classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() | classes.py:1726:18:1726:29 | ControlFlowNode for With_aiter() |
-| classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() | classes.py:1745:18:1745:29 | ControlFlowNode for With_anext() |
-| classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() | classes.py:1763:19:1763:31 | ControlFlowNode for With_aenter() |
-| classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() | classes.py:1784:18:1784:29 | ControlFlowNode for With_aexit() |
diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected
index 0b1dfb6a5be..32020110bcd 100644
--- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected
+++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected
@@ -13,6 +13,11 @@ edges
| datamodel.py:80:6:80:26 | GSSA Variable SOURCE | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE |
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() |
+| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] |
+| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] |
+| datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] |
+| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] |
+| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute |
| test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] |
| test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:37:9:37:12 | ControlFlowNode for Subscript |
@@ -127,6 +132,12 @@ nodes
| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] |
+| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| datamodel.py:155:1:155:10 | GSSA Variable customized [Attribute b] | semmle.label | GSSA Variable customized [Attribute b] |
+| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] |
+| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] |
+| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] |
| test.py:36:21:36:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] |
@@ -263,6 +274,7 @@ nodes
| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | |
| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | |
+| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | |
| test.py:38:10:38:10 | ControlFlowNode for y | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:38:10:38:10 | ControlFlowNode for y | |
| test.py:51:10:51:10 | ControlFlowNode for x | test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | |
| test.py:58:10:58:10 | ControlFlowNode for x | test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | |
diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py
index a3fc54d11a0..f5f3680dd55 100644
--- a/python/ql/test/experimental/dataflow/coverage/datamodel.py
+++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py
@@ -156,4 +156,4 @@ customized = Customized()
SINK(Customized.a)
SINK_F(Customized.b)
SINK(customized.a)
-SINK(customized.b)
+SINK(customized.b) # Flow found
diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
index 7cc18f44d0b..ed0e6a4ea1d 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected
@@ -18,7 +18,6 @@
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj |
| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x |
@@ -26,7 +25,6 @@
| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x |
@@ -34,11 +32,9 @@
| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
index d6b2ba62757..c56353dbd4f 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected
@@ -1,4 +1,8 @@
edges
+| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] |
+| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] |
+| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] |
+| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute |
| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] |
@@ -9,11 +13,16 @@ edges
| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
-| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() |
nodes
+| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | semmle.label | GSSA Variable obj [Attribute foo] |
+| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] |
+| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
+| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
+| examples.py:42:6:42:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] |
@@ -27,13 +36,14 @@ nodes
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | semmle.label | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] |
| test.py:46:10:46:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() |
| test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
#select
+| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | |
| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | |
| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | |
| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
index f65f714063c..1d6e5f42120 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/dataflowExplore.expected
@@ -21,16 +21,16 @@ edges
| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, ... (2)] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] |
| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute |
| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
-| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
-| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
index b0fedf775a3..1add30ba17d 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected
@@ -12,17 +12,18 @@
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
-| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] |
| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] |
| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj |
@@ -37,14 +38,17 @@
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self |
-| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() |
-| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() |
-| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] |
-| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] |
+| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] |
| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] |
+| test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] |
+| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
+| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo |
@@ -101,8 +105,6 @@
| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] |
| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
@@ -111,20 +113,14 @@
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] |
+| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo |
-| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj |
| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
@@ -165,11 +161,12 @@
| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] |
| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
-| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
@@ -180,24 +177,22 @@
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] |
+| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
+| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] |
+| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] |
| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute |
| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute |
| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] |
@@ -237,21 +232,17 @@
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] |
+| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo |
-| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute |
| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow |
@@ -270,26 +261,22 @@
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
-| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] |
+| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
+| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo |
-| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
-| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] |
+| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
index 03549a5b810..93a4d590d01 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected
@@ -2,13 +2,11 @@
| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x |
| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
-| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj |
| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a |
| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a |
| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x |
| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj |
| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a |
| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a |
diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
index 434cd0b6bda..4dcabdd4b86 100644
--- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
+++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected
@@ -1,52 +1,52 @@
| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self |
| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self |
-| examples.py:14:20:14:30 | [post arg] ControlFlowNode for MyObj() | examples.py:14:20:14:30 | ControlFlowNode for MyObj() |
+| examples.py:14:20:14:30 | ControlFlowNode for MyObj() | examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() |
| examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str |
| examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self |
| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj |
| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj |
-| examples.py:25:9:25:19 | [post arg] ControlFlowNode for MyObj() | examples.py:25:9:25:19 | ControlFlowNode for MyObj() |
+| examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:9:25:19 | [pre objCreate] ControlFlowNode for MyObj() |
| examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str |
| examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj |
| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:27:15:27:20 | ControlFlowNode for SOURCE |
| examples.py:28:6:28:10 | [post read] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj |
-| examples.py:33:5:33:15 | [post arg] ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() |
+| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() |
| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a |
| examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute |
| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a |
| examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() |
| examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a |
| examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute |
-| examples.py:41:7:41:19 | [post arg] ControlFlowNode for MyObj() | examples.py:41:7:41:19 | ControlFlowNode for MyObj() |
+| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() |
| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE |
| examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj |
-| examples.py:46:9:46:16 | [post arg] ControlFlowNode for MyObj() | examples.py:46:9:46:16 | ControlFlowNode for MyObj() |
+| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() |
| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x |
| examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj |
| examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE |
| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self |
| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self |
-| test.py:15:20:15:30 | [post arg] ControlFlowNode for MyObj() | test.py:15:20:15:30 | ControlFlowNode for MyObj() |
+| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() |
| test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str |
| test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self |
| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj |
| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj |
-| test.py:27:13:27:23 | [post arg] ControlFlowNode for MyObj() | test.py:27:13:27:23 | ControlFlowNode for MyObj() |
+| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() |
| test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str |
| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj |
| test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE |
| test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj |
-| test.py:36:9:36:19 | [post arg] ControlFlowNode for NestedObj() | test.py:36:9:36:19 | ControlFlowNode for NestedObj() |
+| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() |
| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a |
| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute |
| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a |
| test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() |
| test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a |
| test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute |
-| test.py:45:11:45:23 | [post arg] ControlFlowNode for MyObj() | test.py:45:11:45:23 | ControlFlowNode for MyObj() |
+| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() |
| test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE |
| test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj |
-| test.py:50:11:50:18 | [post arg] ControlFlowNode for MyObj() | test.py:50:11:50:18 | ControlFlowNode for MyObj() |
+| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() |
| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x |
| test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj |
| test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE |
From 08b51e67c42eb257c7e9ad76516021ae206dc07f Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Mon, 21 Sep 2020 17:44:36 +0200
Subject: [PATCH 048/185] Python: Update test annotation
---
python/ql/test/experimental/dataflow/coverage/classes.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py
index 350c10d5b04..830ad72d1c5 100644
--- a/python/ql/test/experimental/dataflow/coverage/classes.py
+++ b/python/ql/test/experimental/dataflow/coverage/classes.py
@@ -50,8 +50,8 @@ def test_new():
class With_init:
def __init__(self):
- SINK1(self) # Flow not found
- OK() # Call not found
+ SINK1(self)
+ OK()
def test_init():
From e70bb20f349ccfd43df7670f290d52dfcb52d734 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Tue, 15 Sep 2020 12:25:07 +0100
Subject: [PATCH 049/185] JS: Support XML extraction when run with codeql
---
.../com/semmle/js/extractor/AutoBuild.java | 29 +++++++++++++++----
.../js/extractor/EnvironmentVariables.java | 14 +++++++++
2 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
index 28c96f806f7..d97dda5daf3 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
@@ -60,6 +60,7 @@ import com.semmle.util.io.WholeIO;
import com.semmle.util.io.csv.CSVReader;
import com.semmle.util.language.LegacyLanguage;
import com.semmle.util.process.Env;
+import com.semmle.util.process.Env.OS;
import com.semmle.util.projectstructure.ProjectLayout;
import com.semmle.util.trap.TrapWriter;
@@ -1239,11 +1240,29 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set
protected void extractXml() throws IOException {
if (xmlExtensions.isEmpty()) return;
List cmd = new ArrayList<>();
- cmd.add("odasa");
- cmd.add("index");
- cmd.add("--xml");
- cmd.add("--extensions");
- cmd.addAll(xmlExtensions);
+ if (EnvironmentVariables.getCodeQLDist() == null) {
+ // Use the legacy odasa XML extractor
+ cmd.add("odasa");
+ cmd.add("index");
+ cmd.add("--xml");
+ cmd.add("--extensions");
+ cmd.addAll(xmlExtensions);
+ } else {
+ String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql";
+ cmd.add(EnvironmentVariables.getCodeQLDist() + "/" + command);
+ cmd.add("database");
+ cmd.add("index-files");
+ cmd.add("--language");
+ cmd.add("xml");
+ cmd.add("--size-limit");
+ cmd.add("10m");
+ for (String extension : xmlExtensions) {
+ cmd.add("--include-extension");
+ cmd.add(extension);
+ }
+ cmd.add("--");
+ cmd.add(EnvironmentVariables.getWipDatabase());
+ }
ProcessBuilder pb = new ProcessBuilder(cmd);
try {
pb.redirectError(Redirect.INHERIT);
diff --git a/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java b/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java
index 6f8e7124b3c..8ffcb65831c 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/EnvironmentVariables.java
@@ -14,6 +14,11 @@ public class EnvironmentVariables {
public static final String LGTM_WORKSPACE_ENV_VAR =
"LGTM_WORKSPACE";
+ public static final String CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR =
+ "CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE";
+
+ public static final String CODEQL_DIST_ENV_VAR = "CODEQL_DIST";
+
/**
* Gets the extractor root based on the CODEQL_EXTRACTOR_JAVASCRIPT_ROOT or
* SEMMLE_DIST or environment variable, or null if neither is set.
@@ -49,4 +54,13 @@ public class EnvironmentVariables {
throw new UserError(CODEQL_EXTRACTOR_JAVASCRIPT_SCRATCH_DIR_ENV_VAR + " or " + LGTM_WORKSPACE_ENV_VAR + " must be set");
}
+
+ public static String getCodeQLDist() {
+ return Env.systemEnv().getNonEmpty(CODEQL_DIST_ENV_VAR);
+ }
+
+ /** Gets the output database directory. */
+ public static String getWipDatabase() {
+ return Env.systemEnv().getNonEmpty(CODEQL_EXTRACTOR_JAVASCRIPT_WIP_DATABASE_ENV_VAR);
+ }
}
From 4243504c8baa3330ee0a7cedd8590704039f6eff Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Mon, 21 Sep 2020 23:20:16 +0200
Subject: [PATCH 050/185] improve join-order for HTTP::isDecoratedCall
---
.../ql/src/semmle/javascript/frameworks/HTTP.qll | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
index e552bbcb85c..28a79edb5e7 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
@@ -240,14 +240,22 @@ module HTTP {
*/
private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) {
// indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
- exists(int i, Function outer, Function inner |
+ exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner |
decoratee = call.getArgument(i).getALocalSource() and
- outer = call.getACallee() and
- inner = outer.getAReturnedExpr() and
- isAForwardingRouteHandlerCall(DataFlow::parameterNode(outer.getParameter(i)), inner.flow())
+ outer.getFunction() = call.getACallee() and
+ outer = returnsARouteHandler(inner) and
+ isAForwardingRouteHandlerCall(outer.getParameter(i), inner)
)
}
+ /**
+ * Gets a function that returns the route-handler-candidate `routeHandler`.
+ */
+ pragma[noinline]
+ private DataFlow::FunctionNode returnsARouteHandler(HTTP::RouteHandlerCandidate routeHandler) {
+ routeHandler = result.getAReturn().getALocalSource()
+ }
+
/**
* Holds if `f` looks like a route-handler and a call to `callee` inside `f` forwards all of the parameters from `f` to that call.
*/
From 913881b17b869602b45e37d5044949e1ba72bde8 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 21 Sep 2020 16:10:37 -0700
Subject: [PATCH 051/185] C++: Add test for iterator false positive
---
.../dataflow/taint-tests/localTaint.expected | 476 ++++++++++--------
.../dataflow/taint-tests/taint.expected | 28 +-
.../dataflow/taint-tests/test_diff.expected | 28 +-
.../dataflow/taint-tests/vector.cpp | 14 +-
4 files changed, 298 insertions(+), 248 deletions(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index ae2bc9b563e..365216bce3c 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3701,224 +3701,258 @@
| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT |
| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT |
| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | |
-| vector.cpp:333:38:333:38 | b | vector.cpp:368:5:368:5 | b | |
-| vector.cpp:334:22:334:24 | call to vector | vector.cpp:336:34:336:35 | v1 | |
-| vector.cpp:334:22:334:24 | call to vector | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:334:22:334:24 | call to vector | vector.cpp:389:1:389:1 | v1 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:38:340:39 | v2 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:340:56:340:57 | v2 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:334:30:334:32 | call to vector | vector.cpp:389:1:389:1 | v2 | |
-| vector.cpp:334:38:334:40 | call to vector | vector.cpp:345:15:345:16 | v3 | |
-| vector.cpp:334:38:334:40 | call to vector | vector.cpp:348:7:348:8 | v3 | |
-| vector.cpp:334:38:334:40 | call to vector | vector.cpp:389:1:389:1 | v3 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:38:350:39 | v4 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:334:46:334:48 | call to vector | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:334:54:334:56 | call to vector | vector.cpp:355:34:355:35 | v5 | |
-| vector.cpp:334:54:334:56 | call to vector | vector.cpp:357:7:357:8 | v5 | |
-| vector.cpp:334:54:334:56 | call to vector | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:334:54:334:56 | call to vector | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:361:34:361:35 | v6 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:363:7:363:8 | v6 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:364:2:364:3 | v6 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:334:62:334:64 | call to vector | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:367:34:367:35 | v7 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:370:8:370:9 | v7 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:373:8:373:9 | v7 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:334:70:334:72 | call to vector | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:334:78:334:80 | call to vector | vector.cpp:377:34:377:35 | v8 | |
-| vector.cpp:334:78:334:80 | call to vector | vector.cpp:379:7:379:8 | v8 | |
-| vector.cpp:334:78:334:80 | call to vector | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:334:78:334:80 | call to vector | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:334:86:334:88 | call to vector | vector.cpp:383:34:383:35 | v9 | |
-| vector.cpp:334:86:334:88 | call to vector | vector.cpp:388:7:388:8 | v9 | |
-| vector.cpp:334:86:334:88 | call to vector | vector.cpp:389:1:389:1 | v9 | |
-| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:336:34:336:35 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | |
-| vector.cpp:336:34:336:35 | v1 | vector.cpp:336:37:336:41 | call to begin | TAINT |
-| vector.cpp:336:37:336:41 | call to begin | vector.cpp:337:3:337:4 | i1 | |
-| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:338:7:338:8 | v1 | |
-| vector.cpp:337:2:337:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v1 | |
-| vector.cpp:337:2:337:15 | ... = ... | vector.cpp:337:2:337:2 | call to operator* [post update] | |
-| vector.cpp:337:3:337:4 | i1 | vector.cpp:337:2:337:2 | call to operator* | TAINT |
-| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:2 | call to operator* [post update] | TAINT |
-| vector.cpp:337:8:337:13 | call to source | vector.cpp:337:2:337:15 | ... = ... | |
-| vector.cpp:338:7:338:8 | ref arg v1 | vector.cpp:389:1:389:1 | v1 | |
-| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
-| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:38:340:39 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
-| vector.cpp:340:38:340:39 | v2 | vector.cpp:340:41:340:45 | call to begin | TAINT |
-| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:50:340:51 | it | |
-| vector.cpp:340:41:340:45 | call to begin | vector.cpp:340:68:340:69 | it | |
-| vector.cpp:340:41:340:45 | call to begin | vector.cpp:341:4:341:5 | it | |
-| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:340:56:340:57 | v2 | |
-| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:340:56:340:57 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
-| vector.cpp:340:56:340:57 | v2 | vector.cpp:340:59:340:61 | call to end | TAINT |
-| vector.cpp:340:68:340:69 | it | vector.cpp:340:66:340:66 | call to operator++ | |
-| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:50:340:51 | it | |
-| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:340:68:340:69 | it | |
-| vector.cpp:340:68:340:69 | ref arg it | vector.cpp:341:4:341:5 | it | |
-| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:340:56:340:57 | v2 | |
-| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:343:7:343:8 | v2 | |
-| vector.cpp:341:3:341:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v2 | |
-| vector.cpp:341:3:341:16 | ... = ... | vector.cpp:341:3:341:3 | call to operator* [post update] | |
-| vector.cpp:341:4:341:5 | it | vector.cpp:341:3:341:3 | call to operator* | TAINT |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:3 | call to operator* [post update] | TAINT |
-| vector.cpp:341:9:341:14 | call to source | vector.cpp:341:3:341:16 | ... = ... | |
-| vector.cpp:343:7:343:8 | ref arg v2 | vector.cpp:389:1:389:1 | v2 | |
-| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator* | TAINT |
-| vector.cpp:345:15:345:15 | (__begin) | vector.cpp:345:15:345:15 | call to operator++ | |
-| vector.cpp:345:15:345:15 | (__end) | vector.cpp:345:15:345:15 | call to iterator | |
-| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to begin | TAINT |
-| vector.cpp:345:15:345:15 | (__range) | vector.cpp:345:15:345:15 | call to end | TAINT |
-| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | call to begin | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | call to end | vector.cpp:345:15:345:15 | (__end) | |
-| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | ref arg (__begin) | vector.cpp:345:15:345:15 | (__begin) | |
-| vector.cpp:345:15:345:15 | ref arg (__range) | vector.cpp:345:15:345:15 | (__range) | |
-| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
-| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | (__range) | |
-| vector.cpp:345:15:345:16 | v3 | vector.cpp:345:15:345:15 | call to operator* | TAINT |
-| vector.cpp:346:3:346:14 | ... = ... | vector.cpp:346:3:346:3 | x [post update] | |
-| vector.cpp:346:7:346:12 | call to source | vector.cpp:346:3:346:14 | ... = ... | |
-| vector.cpp:348:7:348:8 | ref arg v3 | vector.cpp:389:1:389:1 | v3 | |
-| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:38:350:39 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:350:38:350:39 | v4 | vector.cpp:350:41:350:45 | call to begin | TAINT |
-| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:50:350:51 | it | |
-| vector.cpp:350:41:350:45 | call to begin | vector.cpp:350:68:350:69 | it | |
-| vector.cpp:350:41:350:45 | call to begin | vector.cpp:351:32:351:33 | it | |
-| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:350:56:350:57 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:350:56:350:57 | v4 | vector.cpp:350:59:350:61 | call to end | TAINT |
-| vector.cpp:350:68:350:69 | it | vector.cpp:350:66:350:66 | call to operator++ | |
-| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:50:350:51 | it | |
-| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:350:68:350:69 | it | |
-| vector.cpp:350:68:350:69 | ref arg it | vector.cpp:351:32:351:33 | it | |
-| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:351:32:351:33 | call to iterator [post update] | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:351:32:351:33 | it | vector.cpp:351:32:351:33 | call to iterator | |
-| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:350:56:350:57 | v4 | |
-| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:353:7:353:8 | v4 | |
-| vector.cpp:351:32:351:33 | it [post update] | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:353:7:353:8 | ref arg v4 | vector.cpp:389:1:389:1 | v4 | |
-| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:357:7:357:8 | v5 | |
-| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:355:34:355:35 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:355:34:355:35 | v5 | vector.cpp:355:37:355:41 | call to begin | TAINT |
-| vector.cpp:355:37:355:41 | call to begin | vector.cpp:356:3:356:4 | i5 | |
-| vector.cpp:355:37:355:41 | call to begin | vector.cpp:358:3:358:4 | i5 | |
-| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:357:7:357:8 | v5 | |
-| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:356:2:356:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:356:2:356:15 | ... = ... | vector.cpp:356:2:356:2 | call to operator* [post update] | |
-| vector.cpp:356:3:356:4 | i5 | vector.cpp:356:2:356:2 | call to operator* | TAINT |
-| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:2 | call to operator* [post update] | TAINT |
-| vector.cpp:356:8:356:13 | call to source | vector.cpp:356:2:356:15 | ... = ... | |
-| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:357:7:357:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:359:7:359:8 | v5 | |
-| vector.cpp:358:2:358:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:358:2:358:8 | ... = ... | vector.cpp:358:2:358:2 | call to operator* [post update] | |
-| vector.cpp:358:3:358:4 | i5 | vector.cpp:358:2:358:2 | call to operator* | TAINT |
-| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:2 | call to operator* [post update] | TAINT |
-| vector.cpp:358:8:358:8 | 1 | vector.cpp:358:2:358:8 | ... = ... | |
-| vector.cpp:359:7:359:8 | ref arg v5 | vector.cpp:389:1:389:1 | v5 | |
-| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:363:7:363:8 | v6 | |
-| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
-| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:361:34:361:35 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:361:34:361:35 | v6 | vector.cpp:361:37:361:41 | call to begin | TAINT |
-| vector.cpp:361:37:361:41 | call to begin | vector.cpp:362:3:362:4 | i6 | |
-| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v6 | |
-| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:364:2:364:3 | v6 | |
-| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:362:2:362:15 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | |
-| vector.cpp:362:3:362:4 | i6 | vector.cpp:362:2:362:2 | call to operator* | TAINT |
-| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT |
-| vector.cpp:362:8:362:13 | call to source | vector.cpp:362:2:362:15 | ... = ... | |
-| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:364:2:364:3 | v6 | |
-| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:363:7:363:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:365:7:365:8 | v6 | |
-| vector.cpp:364:2:364:3 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:2:364:3 | ref arg v6 | TAINT |
-| vector.cpp:364:7:364:26 | call to vector | vector.cpp:364:5:364:5 | call to operator= | TAINT |
-| vector.cpp:365:7:365:8 | ref arg v6 | vector.cpp:389:1:389:1 | v6 | |
-| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:370:8:370:9 | v7 | |
-| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:373:8:373:9 | v7 | |
-| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:367:34:367:35 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:367:34:367:35 | v7 | vector.cpp:367:37:367:41 | call to begin | TAINT |
-| vector.cpp:367:37:367:41 | call to begin | vector.cpp:369:4:369:5 | i7 | |
-| vector.cpp:367:37:367:41 | call to begin | vector.cpp:372:4:372:5 | i7 | |
-| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:370:8:370:9 | v7 | |
-| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:369:3:369:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:369:3:369:16 | ... = ... | vector.cpp:369:3:369:3 | call to operator* [post update] | |
-| vector.cpp:369:4:369:5 | i7 | vector.cpp:369:3:369:3 | call to operator* | TAINT |
-| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:3 | call to operator* [post update] | TAINT |
-| vector.cpp:369:9:369:14 | call to source | vector.cpp:369:3:369:16 | ... = ... | |
-| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:370:8:370:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:373:8:373:9 | v7 | |
-| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:372:3:372:3 | call to operator* [post update] | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:372:3:372:9 | ... = ... | vector.cpp:372:3:372:3 | call to operator* [post update] | |
-| vector.cpp:372:4:372:5 | i7 | vector.cpp:372:3:372:3 | call to operator* | TAINT |
-| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:3 | call to operator* [post update] | TAINT |
-| vector.cpp:372:9:372:9 | 1 | vector.cpp:372:3:372:9 | ... = ... | |
-| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:375:7:375:8 | v7 | |
-| vector.cpp:373:8:373:9 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:375:7:375:8 | ref arg v7 | vector.cpp:389:1:389:1 | v7 | |
-| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:379:7:379:8 | v8 | |
-| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:377:34:377:35 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:377:34:377:35 | v8 | vector.cpp:377:37:377:41 | call to begin | TAINT |
-| vector.cpp:377:37:377:41 | call to begin | vector.cpp:378:3:378:4 | i8 | |
-| vector.cpp:377:37:377:41 | call to begin | vector.cpp:380:3:380:4 | i8 | |
-| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:379:7:379:8 | v8 | |
-| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:378:2:378:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:378:2:378:15 | ... = ... | vector.cpp:378:2:378:2 | call to operator* [post update] | |
-| vector.cpp:378:3:378:4 | i8 | vector.cpp:378:2:378:2 | call to operator* | TAINT |
-| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:2 | call to operator* [post update] | TAINT |
-| vector.cpp:378:8:378:13 | call to source | vector.cpp:378:2:378:15 | ... = ... | |
-| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:379:7:379:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:381:7:381:8 | v8 | |
-| vector.cpp:380:2:380:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:380:2:380:8 | ... = ... | vector.cpp:380:2:380:2 | call to operator* [post update] | |
-| vector.cpp:380:3:380:4 | i8 | vector.cpp:380:2:380:2 | call to operator* | TAINT |
-| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:2 | call to operator* [post update] | TAINT |
-| vector.cpp:380:8:380:8 | 1 | vector.cpp:380:2:380:8 | ... = ... | |
-| vector.cpp:381:7:381:8 | ref arg v8 | vector.cpp:389:1:389:1 | v8 | |
-| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:388:7:388:8 | v9 | |
-| vector.cpp:383:34:383:35 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | |
-| vector.cpp:383:34:383:35 | v9 | vector.cpp:383:37:383:41 | call to begin | TAINT |
-| vector.cpp:383:37:383:41 | call to begin | vector.cpp:385:3:385:4 | i9 | |
-| vector.cpp:383:37:383:41 | call to begin | vector.cpp:386:31:386:32 | i9 | |
-| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:388:7:388:8 | v9 | |
-| vector.cpp:385:2:385:2 | call to operator* [post update] | vector.cpp:389:1:389:1 | v9 | |
-| vector.cpp:385:2:385:15 | ... = ... | vector.cpp:385:2:385:2 | call to operator* [post update] | |
-| vector.cpp:385:3:385:4 | i9 | vector.cpp:385:2:385:2 | call to operator* | TAINT |
-| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:2 | call to operator* [post update] | TAINT |
-| vector.cpp:385:8:385:13 | call to source | vector.cpp:385:2:385:15 | ... = ... | |
-| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:388:7:388:8 | v9 | |
-| vector.cpp:386:31:386:32 | call to iterator [post update] | vector.cpp:389:1:389:1 | v9 | |
-| vector.cpp:386:31:386:32 | i9 | vector.cpp:386:31:386:32 | call to iterator | |
-| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:388:7:388:8 | v9 | |
-| vector.cpp:386:31:386:32 | i9 [post update] | vector.cpp:389:1:389:1 | v9 | |
-| vector.cpp:388:7:388:8 | ref arg v9 | vector.cpp:389:1:389:1 | v9 | |
+| vector.cpp:333:64:333:67 | iter | vector.cpp:333:64:333:67 | iter | |
+| vector.cpp:333:64:333:67 | iter | vector.cpp:334:3:334:6 | iter | |
+| vector.cpp:333:74:333:74 | i | vector.cpp:334:10:334:10 | i | |
+| vector.cpp:334:2:334:2 | call to operator* [post update] | vector.cpp:333:64:333:67 | iter | |
+| vector.cpp:334:2:334:10 | ... = ... | vector.cpp:334:2:334:2 | call to operator* [post update] | |
+| vector.cpp:334:3:334:6 | iter | vector.cpp:334:2:334:2 | call to operator* | TAINT |
+| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:2 | call to operator* [post update] | TAINT |
+| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:10 | ... = ... | |
+| vector.cpp:337:38:337:38 | b | vector.cpp:372:5:372:5 | b | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:340:34:340:35 | v1 | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:38:344:39 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:349:15:349:16 | v3 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:352:7:352:8 | v3 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:401:1:401:1 | v3 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:38:354:39 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:359:34:359:35 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:365:34:365:35 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:371:34:371:35 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:381:34:381:35 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:387:34:387:35 | v9 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:394:35:394:37 | v10 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:398:35:398:37 | v11 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:340:34:340:35 | v1 | vector.cpp:340:37:340:41 | call to begin | TAINT |
+| vector.cpp:340:37:340:41 | call to begin | vector.cpp:341:3:341:4 | i1 | |
+| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:341:2:341:15 | ... = ... | vector.cpp:341:2:341:2 | call to operator* [post update] | |
+| vector.cpp:341:3:341:4 | i1 | vector.cpp:341:2:341:2 | call to operator* | TAINT |
+| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:2 | call to operator* [post update] | TAINT |
+| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:15 | ... = ... | |
+| vector.cpp:342:7:342:8 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:344:38:344:39 | v2 | vector.cpp:344:41:344:45 | call to begin | TAINT |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:50:344:51 | it | |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:68:344:69 | it | |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:345:4:345:5 | it | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:344:56:344:57 | v2 | vector.cpp:344:59:344:61 | call to end | TAINT |
+| vector.cpp:344:68:344:69 | it | vector.cpp:344:66:344:66 | call to operator++ | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:50:344:51 | it | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:68:344:69 | it | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:345:4:345:5 | it | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:345:3:345:16 | ... = ... | vector.cpp:345:3:345:3 | call to operator* [post update] | |
+| vector.cpp:345:4:345:5 | it | vector.cpp:345:3:345:3 | call to operator* | TAINT |
+| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:3 | call to operator* [post update] | TAINT |
+| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:16 | ... = ... | |
+| vector.cpp:347:7:347:8 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator* | TAINT |
+| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator++ | |
+| vector.cpp:349:15:349:15 | (__end) | vector.cpp:349:15:349:15 | call to iterator | |
+| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to begin | TAINT |
+| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to end | TAINT |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to end | vector.cpp:349:15:349:15 | (__end) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__range) | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | call to operator* | TAINT |
+| vector.cpp:350:3:350:14 | ... = ... | vector.cpp:350:3:350:3 | x [post update] | |
+| vector.cpp:350:7:350:12 | call to source | vector.cpp:350:3:350:14 | ... = ... | |
+| vector.cpp:352:7:352:8 | ref arg v3 | vector.cpp:401:1:401:1 | v3 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:354:38:354:39 | v4 | vector.cpp:354:41:354:45 | call to begin | TAINT |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:50:354:51 | it | |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:68:354:69 | it | |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:355:32:355:33 | it | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:354:56:354:57 | v4 | vector.cpp:354:59:354:61 | call to end | TAINT |
+| vector.cpp:354:68:354:69 | it | vector.cpp:354:66:354:66 | call to operator++ | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:50:354:51 | it | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:68:354:69 | it | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:355:32:355:33 | it | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:355:32:355:33 | it | vector.cpp:355:32:355:33 | call to iterator | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:357:7:357:8 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:359:34:359:35 | v5 | vector.cpp:359:37:359:41 | call to begin | TAINT |
+| vector.cpp:359:37:359:41 | call to begin | vector.cpp:360:3:360:4 | i5 | |
+| vector.cpp:359:37:359:41 | call to begin | vector.cpp:362:3:362:4 | i5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:360:2:360:15 | ... = ... | vector.cpp:360:2:360:2 | call to operator* [post update] | |
+| vector.cpp:360:3:360:4 | i5 | vector.cpp:360:2:360:2 | call to operator* | TAINT |
+| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:2 | call to operator* [post update] | TAINT |
+| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:15 | ... = ... | |
+| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:362:2:362:8 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | |
+| vector.cpp:362:3:362:4 | i5 | vector.cpp:362:2:362:2 | call to operator* | TAINT |
+| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT |
+| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:8 | ... = ... | |
+| vector.cpp:363:7:363:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:365:34:365:35 | v6 | vector.cpp:365:37:365:41 | call to begin | TAINT |
+| vector.cpp:365:37:365:41 | call to begin | vector.cpp:366:3:366:4 | i6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:366:2:366:15 | ... = ... | vector.cpp:366:2:366:2 | call to operator* [post update] | |
+| vector.cpp:366:3:366:4 | i6 | vector.cpp:366:2:366:2 | call to operator* | TAINT |
+| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:2 | call to operator* [post update] | TAINT |
+| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:15 | ... = ... | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:2:368:3 | ref arg v6 | TAINT |
+| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:5:368:5 | call to operator= | TAINT |
+| vector.cpp:369:7:369:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:371:34:371:35 | v7 | vector.cpp:371:37:371:41 | call to begin | TAINT |
+| vector.cpp:371:37:371:41 | call to begin | vector.cpp:373:4:373:5 | i7 | |
+| vector.cpp:371:37:371:41 | call to begin | vector.cpp:376:4:376:5 | i7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:373:3:373:16 | ... = ... | vector.cpp:373:3:373:3 | call to operator* [post update] | |
+| vector.cpp:373:4:373:5 | i7 | vector.cpp:373:3:373:3 | call to operator* | TAINT |
+| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:3 | call to operator* [post update] | TAINT |
+| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:16 | ... = ... | |
+| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:376:3:376:9 | ... = ... | vector.cpp:376:3:376:3 | call to operator* [post update] | |
+| vector.cpp:376:4:376:5 | i7 | vector.cpp:376:3:376:3 | call to operator* | TAINT |
+| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:3 | call to operator* [post update] | TAINT |
+| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:9 | ... = ... | |
+| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:379:7:379:8 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:381:34:381:35 | v8 | vector.cpp:381:37:381:41 | call to begin | TAINT |
+| vector.cpp:381:37:381:41 | call to begin | vector.cpp:382:3:382:4 | i8 | |
+| vector.cpp:381:37:381:41 | call to begin | vector.cpp:384:3:384:4 | i8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:382:2:382:15 | ... = ... | vector.cpp:382:2:382:2 | call to operator* [post update] | |
+| vector.cpp:382:3:382:4 | i8 | vector.cpp:382:2:382:2 | call to operator* | TAINT |
+| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:2 | call to operator* [post update] | TAINT |
+| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:15 | ... = ... | |
+| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:384:2:384:8 | ... = ... | vector.cpp:384:2:384:2 | call to operator* [post update] | |
+| vector.cpp:384:3:384:4 | i8 | vector.cpp:384:2:384:2 | call to operator* | TAINT |
+| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:2 | call to operator* [post update] | TAINT |
+| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:8 | ... = ... | |
+| vector.cpp:385:7:385:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:387:34:387:35 | v9 | vector.cpp:387:37:387:41 | call to begin | TAINT |
+| vector.cpp:387:37:387:41 | call to begin | vector.cpp:389:3:389:4 | i9 | |
+| vector.cpp:387:37:387:41 | call to begin | vector.cpp:390:31:390:32 | i9 | |
+| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:389:2:389:15 | ... = ... | vector.cpp:389:2:389:2 | call to operator* [post update] | |
+| vector.cpp:389:3:389:4 | i9 | vector.cpp:389:2:389:2 | call to operator* | TAINT |
+| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:2 | call to operator* [post update] | TAINT |
+| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:15 | ... = ... | |
+| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:390:31:390:32 | i9 | vector.cpp:390:31:390:32 | call to iterator | |
+| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:392:7:392:8 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:394:35:394:37 | v10 | vector.cpp:394:39:394:43 | call to begin | TAINT |
+| vector.cpp:394:39:394:43 | call to begin | vector.cpp:395:33:395:35 | i10 | |
+| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:395:33:395:35 | i10 | vector.cpp:395:33:395:35 | call to iterator | |
+| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:396:7:396:9 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:398:35:398:37 | v11 | vector.cpp:398:39:398:43 | call to begin | TAINT |
+| vector.cpp:398:39:398:43 | call to begin | vector.cpp:399:33:399:35 | i11 | |
+| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:399:33:399:35 | i11 | vector.cpp:399:33:399:35 | call to iterator | |
+| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:400:7:400:9 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 2eca13801c6..f91b718bdda 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -375,16 +375,18 @@
| vector.cpp:312:7:312:7 | d | vector.cpp:303:14:303:19 | call to source |
| vector.cpp:324:7:324:8 | v2 | vector.cpp:318:15:318:20 | call to source |
| vector.cpp:326:7:326:8 | v4 | vector.cpp:318:15:318:20 | call to source |
-| vector.cpp:338:7:338:8 | v1 | vector.cpp:337:8:337:13 | call to source |
-| vector.cpp:343:7:343:8 | v2 | vector.cpp:341:9:341:14 | call to source |
-| vector.cpp:353:7:353:8 | v4 | vector.cpp:330:10:330:15 | call to source |
-| vector.cpp:357:7:357:8 | v5 | vector.cpp:356:8:356:13 | call to source |
-| vector.cpp:359:7:359:8 | v5 | vector.cpp:356:8:356:13 | call to source |
-| vector.cpp:363:7:363:8 | v6 | vector.cpp:362:8:362:13 | call to source |
-| vector.cpp:365:7:365:8 | v6 | vector.cpp:362:8:362:13 | call to source |
-| vector.cpp:370:8:370:9 | v7 | vector.cpp:369:9:369:14 | call to source |
-| vector.cpp:375:7:375:8 | v7 | vector.cpp:369:9:369:14 | call to source |
-| vector.cpp:379:7:379:8 | v8 | vector.cpp:378:8:378:13 | call to source |
-| vector.cpp:381:7:381:8 | v8 | vector.cpp:378:8:378:13 | call to source |
-| vector.cpp:388:7:388:8 | v9 | vector.cpp:330:10:330:15 | call to source |
-| vector.cpp:388:7:388:8 | v9 | vector.cpp:385:8:385:13 | call to source |
+| vector.cpp:342:7:342:8 | v1 | vector.cpp:341:8:341:13 | call to source |
+| vector.cpp:347:7:347:8 | v2 | vector.cpp:345:9:345:14 | call to source |
+| vector.cpp:357:7:357:8 | v4 | vector.cpp:330:10:330:15 | call to source |
+| vector.cpp:361:7:361:8 | v5 | vector.cpp:360:8:360:13 | call to source |
+| vector.cpp:363:7:363:8 | v5 | vector.cpp:360:8:360:13 | call to source |
+| vector.cpp:367:7:367:8 | v6 | vector.cpp:366:8:366:13 | call to source |
+| vector.cpp:369:7:369:8 | v6 | vector.cpp:366:8:366:13 | call to source |
+| vector.cpp:374:8:374:9 | v7 | vector.cpp:373:9:373:14 | call to source |
+| vector.cpp:379:7:379:8 | v7 | vector.cpp:373:9:373:14 | call to source |
+| vector.cpp:383:7:383:8 | v8 | vector.cpp:382:8:382:13 | call to source |
+| vector.cpp:385:7:385:8 | v8 | vector.cpp:382:8:382:13 | call to source |
+| vector.cpp:392:7:392:8 | v9 | vector.cpp:330:10:330:15 | call to source |
+| vector.cpp:392:7:392:8 | v9 | vector.cpp:389:8:389:13 | call to source |
+| vector.cpp:396:7:396:9 | v10 | vector.cpp:399:38:399:43 | call to source |
+| vector.cpp:400:7:400:9 | v11 | vector.cpp:399:38:399:43 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index db6bf0f3cf0..6397c1d7830 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -300,16 +300,18 @@
| vector.cpp:312:7:312:7 | vector.cpp:303:14:303:19 | AST only |
| vector.cpp:324:7:324:8 | vector.cpp:318:15:318:20 | AST only |
| vector.cpp:326:7:326:8 | vector.cpp:318:15:318:20 | AST only |
-| vector.cpp:338:7:338:8 | vector.cpp:337:8:337:13 | AST only |
-| vector.cpp:343:7:343:8 | vector.cpp:341:9:341:14 | AST only |
-| vector.cpp:353:7:353:8 | vector.cpp:330:10:330:15 | AST only |
-| vector.cpp:357:7:357:8 | vector.cpp:356:8:356:13 | AST only |
-| vector.cpp:359:7:359:8 | vector.cpp:356:8:356:13 | AST only |
-| vector.cpp:363:7:363:8 | vector.cpp:362:8:362:13 | AST only |
-| vector.cpp:365:7:365:8 | vector.cpp:362:8:362:13 | AST only |
-| vector.cpp:370:8:370:9 | vector.cpp:369:9:369:14 | AST only |
-| vector.cpp:375:7:375:8 | vector.cpp:369:9:369:14 | AST only |
-| vector.cpp:379:7:379:8 | vector.cpp:378:8:378:13 | AST only |
-| vector.cpp:381:7:381:8 | vector.cpp:378:8:378:13 | AST only |
-| vector.cpp:388:7:388:8 | vector.cpp:330:10:330:15 | AST only |
-| vector.cpp:388:7:388:8 | vector.cpp:385:8:385:13 | AST only |
+| vector.cpp:342:7:342:8 | vector.cpp:341:8:341:13 | AST only |
+| vector.cpp:347:7:347:8 | vector.cpp:345:9:345:14 | AST only |
+| vector.cpp:357:7:357:8 | vector.cpp:330:10:330:15 | AST only |
+| vector.cpp:361:7:361:8 | vector.cpp:360:8:360:13 | AST only |
+| vector.cpp:363:7:363:8 | vector.cpp:360:8:360:13 | AST only |
+| vector.cpp:367:7:367:8 | vector.cpp:366:8:366:13 | AST only |
+| vector.cpp:369:7:369:8 | vector.cpp:366:8:366:13 | AST only |
+| vector.cpp:374:8:374:9 | vector.cpp:373:9:373:14 | AST only |
+| vector.cpp:379:7:379:8 | vector.cpp:373:9:373:14 | AST only |
+| vector.cpp:383:7:383:8 | vector.cpp:382:8:382:13 | AST only |
+| vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only |
+| vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only |
+| vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only |
+| vector.cpp:396:7:396:9 | vector.cpp:399:38:399:43 | AST only |
+| vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 92cd5f4c9f5..0cc6570a175 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -330,8 +330,12 @@ void taint_vector_output_iterator(std::vector::iterator iter) {
*iter = source();
}
+void vector_iterator_assign_wrapper(std::vector::iterator iter, int i) {
+ *iter = i;
+}
+
void test_vector_output_iterator(int b) {
- std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10);
+ std::vector v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10), v10(10), v11(10);
std::vector::iterator i1 = v1.begin();
*i1 = source();
@@ -386,4 +390,12 @@ void test_vector_output_iterator(int b) {
taint_vector_output_iterator(i9);
sink(v9);
+
+ std::vector::iterator i10 = v10.begin();
+ vector_iterator_assign_wrapper(i10, 10);
+ sink(v10); // FALSE POSITIVE
+
+ std::vector::iterator i11 = v11.begin();
+ vector_iterator_assign_wrapper(i11, source());
+ sink(v11); // tainted [NOT DETECTED by IR]
}
From 9e3bfe1968c3c39339bf638ce47a709c9a1effc4 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 21 Sep 2020 16:17:16 -0700
Subject: [PATCH 052/185] C++: Fix iterator flow context sensitivity
---
.../cpp/dataflow/internal/DataFlowPrivate.qll | 6 ++-
.../cpp/dataflow/internal/DataFlowUtil.qll | 42 ++++++++++++-------
.../code/cpp/dataflow/internal/FlowVar.qll | 7 ++++
.../dataflow/internal/TaintTrackingUtil.qll | 5 ---
.../dataflow/taint-tests/taint.expected | 1 -
.../dataflow/taint-tests/test_diff.expected | 1 -
6 files changed, 37 insertions(+), 25 deletions(-)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index 42c31bca69c..e737f8b2d94 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -1,6 +1,8 @@
private import cpp
private import DataFlowUtil
private import DataFlowDispatch
+private import FlowVar
+
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
@@ -106,7 +108,7 @@ private class ExprOutNode extends OutNode, ExprNode {
override DataFlowCall getCall() { result = this.getExpr() }
}
-private class RefOutNode extends OutNode, DefinitionByReferenceNode {
+private class RefOutNode extends OutNode, DefinitionByReferenceOrIteratorNode {
/** Gets the underlying call. */
override DataFlowCall getCall() { result = this.getArgument().getParent() }
}
@@ -120,7 +122,7 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
kind = TNormalReturnKind()
or
exists(int i |
- result.asDefiningArgument() = call.getArgument(i) and
+ result.(DefinitionByReferenceOrIteratorNode).getArgument() = call.getArgument(i) and
kind = TRefReturnKind(i)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 963f1a17826..25a37f9db0f 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -182,29 +182,23 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 }
}
-/**
- * A node that represents the value of a variable after a function call that
- * may have changed the variable because it's passed by reference.
- *
- * A typical example would be a call `f(&x)`. Firstly, there will be flow into
- * `x` from previous definitions of `x`. Secondly, there will be a
- * `DefinitionByReferenceNode` to represent the value of `x` after the call has
- * returned. This node will have its `getArgument()` equal to `&x`.
- */
-class DefinitionByReferenceNode extends PartialDefinitionNode {
+class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode {
Expr inner;
Expr argument;
- DefinitionByReferenceNode() {
- this.getPartialDefinition().(DefinitionByReference).definesExpressions(inner, argument)
+ DefinitionByReferenceOrIteratorNode() {
+ this.getPartialDefinition().definesExpressions(inner, argument) and
+ (
+ this.getPartialDefinition() instanceof DefinitionByReference
+ or
+ this.getPartialDefinition() instanceof DefinitionByIterator
+ )
}
override Function getFunction() { result = inner.getEnclosingFunction() }
override Type getType() { result = inner.getType() }
- override string toString() { result = "ref arg " + argument.toString() }
-
override Location getLocation() { result = argument.getLocation() }
override ExprNode getPreUpdateNode() { result.getExpr() = argument }
@@ -221,6 +215,22 @@ class DefinitionByReferenceNode extends PartialDefinitionNode {
}
}
+
+/**
+ * A node that represents the value of a variable after a function call that
+ * may have changed the variable because it's passed by reference.
+ *
+ * A typical example would be a call `f(&x)`. Firstly, there will be flow into
+ * `x` from previous definitions of `x`. Secondly, there will be a
+ * `DefinitionByReferenceNode` to represent the value of `x` after the call has
+ * returned. This node will have its `getArgument()` equal to `&x`.
+ */
+class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode {
+ override VariablePartialDefinition pd;
+
+ override string toString() { result = "ref arg " + argument.toString() }
+}
+
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
@@ -551,10 +561,10 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
// from which there is field flow to `x` via reverse read.
- exists(VariablePartialDefinition def, Expr inner, Expr outer |
+ exists(PartialDefinition def, Expr inner, Expr outer |
def.definesExpressions(inner, outer) and
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
- outer = nodeFrom.(VariablePartialDefinitionNode).getPreUpdateNode().asExpr()
+ outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
)
or
// Reverse flow: data that flows from the post-update node of a reference
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 13f11f8b275..15c15595c4f 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -243,6 +243,13 @@ private module PartialDefinitions {
}
}
+ /**
+ * A partial definition that's a definition by reference.
+ */
+ class DefinitionByIterator extends IteratorPartialDefinition {
+ DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
+ }
+
/**
* A partial definition that's a definition by reference.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index 3190d3e9eba..17cb9b88104 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -31,11 +31,6 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
*/
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
- or
- exists(FunctionCall call, int i |
- sink.(DataFlow::IteratorPartialDefinitionNode).getPartialDefinition().definesExpressions(_, call.getArgument(i)) and
- src.(DataFlow::RefParameterFinalValueNode).getParameter() = call.getTarget().getParameter(i)
- )
}
/**
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index f91b718bdda..033cd47145b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -388,5 +388,4 @@
| vector.cpp:385:7:385:8 | v8 | vector.cpp:382:8:382:13 | call to source |
| vector.cpp:392:7:392:8 | v9 | vector.cpp:330:10:330:15 | call to source |
| vector.cpp:392:7:392:8 | v9 | vector.cpp:389:8:389:13 | call to source |
-| vector.cpp:396:7:396:9 | v10 | vector.cpp:399:38:399:43 | call to source |
| vector.cpp:400:7:400:9 | v11 | vector.cpp:399:38:399:43 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 6397c1d7830..3dc9b717e07 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -313,5 +313,4 @@
| vector.cpp:385:7:385:8 | vector.cpp:382:8:382:13 | AST only |
| vector.cpp:392:7:392:8 | vector.cpp:330:10:330:15 | AST only |
| vector.cpp:392:7:392:8 | vector.cpp:389:8:389:13 | AST only |
-| vector.cpp:396:7:396:9 | vector.cpp:399:38:399:43 | AST only |
| vector.cpp:400:7:400:9 | vector.cpp:399:38:399:43 | AST only |
From 772a51508f09f8b8b7d2e256db11dab0fbfd33f6 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Mon, 21 Sep 2020 16:19:41 -0700
Subject: [PATCH 053/185] C++: Update test comment
---
cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 0cc6570a175..b5dad811998 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -393,7 +393,7 @@ void test_vector_output_iterator(int b) {
std::vector::iterator i10 = v10.begin();
vector_iterator_assign_wrapper(i10, 10);
- sink(v10); // FALSE POSITIVE
+ sink(v10);
std::vector::iterator i11 = v11.begin();
vector_iterator_assign_wrapper(i11, source());
From d8176bc00de78f51107f68bd73cc5b84a5549ee0 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 22 Sep 2020 09:53:35 +0200
Subject: [PATCH 054/185] C#: Change TrapStackBehaviour of local functions
---
.../Semmle.Extraction.CSharp/Entities/LocalFunction.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs
index 8b90f6905dd..edce7b66ce2 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs
@@ -51,5 +51,7 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.local_functions(this, symbol.Name, returnType, originalDefinition);
ExtractRefReturn(trapFile);
}
+
+ public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel;
}
}
From ec49c444ef99291d0b9f080c944dac1f43477649 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 22 Sep 2020 10:06:34 +0200
Subject: [PATCH 055/185] Apply suggestions from code review
Co-authored-by: Esben Sparre Andreasen
---
.../ql/src/semmle/javascript/frameworks/HTTP.qll | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
index 28a79edb5e7..bf13d8243c0 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
@@ -243,17 +243,19 @@ module HTTP {
exists(int i, DataFlow::FunctionNode outer, HTTP::RouteHandlerCandidate inner |
decoratee = call.getArgument(i).getALocalSource() and
outer.getFunction() = call.getACallee() and
- outer = returnsARouteHandler(inner) and
+ returnsRouteHandler(outer, inner) and
isAForwardingRouteHandlerCall(outer.getParameter(i), inner)
)
}
/**
- * Gets a function that returns the route-handler-candidate `routeHandler`.
+ * Holds if `fun` returns the route-handler-candidate `routeHandler`.
*/
pragma[noinline]
- private DataFlow::FunctionNode returnsARouteHandler(HTTP::RouteHandlerCandidate routeHandler) {
- routeHandler = result.getAReturn().getALocalSource()
+ private predicate returnsRouteHandler(
+ DataFlow::FunctionNode fun, HTTP::RouteHandlerCandidate routeHandler
+ ) {
+ routeHandler = fun.getAReturn().getALocalSource()
}
/**
From 46ba4a1fa8ee4a8494fa2bdc5624b5c1ad995de7 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Thu, 17 Sep 2020 11:58:01 +0100
Subject: [PATCH 056/185] JavaScript: Expose another useful predicate on
API-graph nodes.
---
javascript/ql/src/semmle/javascript/ApiGraphs.qll | 3 +++
1 file changed, 3 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
index 4c92212df57..160ff9e47a2 100644
--- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll
@@ -223,6 +223,9 @@ module API {
) and
length in [1 .. Impl::distanceFromRoot(this)]
}
+
+ /** Gets the shortest distance from the root to this node in the API graph. */
+ int getDepth() { result = Impl::distanceFromRoot(this) }
}
/** The root node of an API graph. */
From dafd45f0f4c056e5d8ce199db111050c935f6b08 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Thu, 17 Sep 2020 12:07:00 +0100
Subject: [PATCH 057/185] JavaScript: Add a few metric queries for API graphs.
---
javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql | 15 +++++++++++++++
javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql | 14 ++++++++++++++
.../src/meta/ApiGraphs/ApiGraphPointsToEdges.ql | 14 ++++++++++++++
.../ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql | 15 +++++++++++++++
.../ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql | 14 ++++++++++++++
5 files changed, 72 insertions(+)
create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql
create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql
create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql
create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql
create mode 100644 javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql
diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql
new file mode 100644
index 00000000000..16718a9d122
--- /dev/null
+++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphEdges.ql
@@ -0,0 +1,15 @@
+/**
+ * @name API-graph edges
+ * @description The number of edges (other than points-to edges) in the API graph.
+ * @kind metric
+ * @metricType project
+ * @metricAggregate sum
+ * @tags meta
+ * @id js/meta/api-graph-edges
+ */
+
+import javascript
+import meta.MetaMetrics
+
+select projectRoot(),
+ count(API::Node pred, string lbl, API::Node succ | succ = pred.getASuccessor(lbl))
diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql
new file mode 100644
index 00000000000..661c1414e1d
--- /dev/null
+++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphNodes.ql
@@ -0,0 +1,14 @@
+/**
+ * @name API-graph nodes
+ * @description The number of nodes in the API graph.
+ * @kind metric
+ * @metricType project
+ * @metricAggregate sum
+ * @tags meta
+ * @id js/meta/api-graph-nodes
+ */
+
+import javascript
+import meta.MetaMetrics
+
+select projectRoot(), count(API::Node nd)
diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql
new file mode 100644
index 00000000000..e410af5a4f1
--- /dev/null
+++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphPointsToEdges.ql
@@ -0,0 +1,14 @@
+/**
+ * @name API-graph points-to edges
+ * @description The number of points-to edges in the API graph.
+ * @kind metric
+ * @metricType project
+ * @metricAggregate sum
+ * @tags meta
+ * @id js/meta/api-graph-points-to-edges
+ */
+
+import javascript
+import meta.MetaMetrics
+
+select projectRoot(), count(API::Node pred, API::Node succ | pred.refersTo(succ))
diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql
new file mode 100644
index 00000000000..fcb98dc57af
--- /dev/null
+++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphRhsNodes.ql
@@ -0,0 +1,15 @@
+/**
+ * @name API-graph right-hand-side nodes
+ * @description The number of data-flow nodes corresponding to a right-hand side of
+ * a definition of an API-graph node.
+ * @kind metric
+ * @metricType project
+ * @metricAggregate sum
+ * @tags meta
+ * @id js/meta/api-graph-rhs-nodes
+ */
+
+import javascript
+import meta.MetaMetrics
+
+select projectRoot(), count(any(API::Node nd).getARhs())
diff --git a/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql
new file mode 100644
index 00000000000..446df101364
--- /dev/null
+++ b/javascript/ql/src/meta/ApiGraphs/ApiGraphUseNodes.ql
@@ -0,0 +1,14 @@
+/**
+ * @name API-graph use nodes
+ * @description The number of data-flow nodes corresponding to a use of an API-graph node.
+ * @kind metric
+ * @metricType project
+ * @metricAggregate sum
+ * @tags meta
+ * @id js/meta/api-graph-use-nodes
+ */
+
+import javascript
+import meta.MetaMetrics
+
+select projectRoot(), count(any(API::Node nd).getAUse())
From 32b0f1b480a279573687ac277023b727a6c657f6 Mon Sep 17 00:00:00 2001
From: Erik Krogh Kristensen
Date: Tue, 22 Sep 2020 10:28:43 +0200
Subject: [PATCH 058/185] add code example to isDecoratedCall
---
.../ql/src/semmle/javascript/frameworks/HTTP.qll | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
index bf13d8243c0..0f224ff060f 100644
--- a/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
+++ b/javascript/ql/src/semmle/javascript/frameworks/HTTP.qll
@@ -237,6 +237,18 @@ module HTTP {
* Holds if `call` decorates the function `pred`.
* This means that `call` returns a function that forwards its arguments to `pred`.
* Only holds when the decorator looks like it is decorating a route-handler.
+ *
+ * Below is a code example relating `call`, `decoratee`, `outer`, `inner`.
+ * ```
+ * function outer(method) {
+ * return function inner(req, res) {
+ * return method.call(this, req, res);
+ * };
+ * }
+ * var route = outer(function decoratee(req, res) { // <- call
+ * res.end("foo");
+ * });
+ * ```
*/
private predicate isDecoratedCall(DataFlow::CallNode call, DataFlow::FunctionNode decoratee) {
// indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
From 71da9045e5361c261c1acb5af5ddb7bdfbcbd828 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 22 Sep 2020 11:00:26 +0200
Subject: [PATCH 059/185] Java/Python: Reduce size of `blockPrecedesVar`
---
csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll | 6 +++---
java/ql/src/semmle/code/java/dataflow/SSA.qll | 4 +++-
java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll | 4 +++-
python/ql/src/semmle/python/essa/SsaCompute.qll | 4 +++-
4 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
index adcfdfaaca7..899165f5f34 100644
--- a/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
+++ b/csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll
@@ -706,9 +706,9 @@ module Ssa {
/**
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
- * `bb2` is a transitive successor of `bb1`, and `def` is *maybe* read in `bb2`
- * or one of its transitive successors, but not in any block on the path between
- * `bb1` and `bb2`.
+ * `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`,
+ * and the underlying variable for `def` is neither read nor written in any block
+ * on the path between `bb1` and `bb2`.
*/
private predicate varBlockReaches(TrackedDefinition def, BasicBlock bb1, BasicBlock bb2) {
varOccursInBlock(def, bb1, _) and
diff --git a/java/ql/src/semmle/code/java/dataflow/SSA.qll b/java/ql/src/semmle/code/java/dataflow/SSA.qll
index 84d6f179efb..851427c4b26 100644
--- a/java/ql/src/semmle/code/java/dataflow/SSA.qll
+++ b/java/ql/src/semmle/code/java/dataflow/SSA.qll
@@ -770,7 +770,9 @@ private module SsaImpl {
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
private predicate blockPrecedesVar(TrackedVar v, BasicBlock b) {
- varOccursInBlock(v, b.getABBSuccessor*())
+ varOccursInBlock(v, b)
+ or
+ ssaDefReachesEndOfBlock(v, _, b)
}
/**
diff --git a/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll
index 9c911a2782c..58021120b5c 100644
--- a/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll
+++ b/java/ql/src/semmle/code/java/dataflow/internal/BaseSSA.qll
@@ -321,7 +321,9 @@ private module SsaImpl {
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
private predicate blockPrecedesVar(BaseSsaSourceVariable v, BasicBlock b) {
- varOccursInBlock(v, b.getABBSuccessor*())
+ varOccursInBlock(v, b)
+ or
+ ssaDefReachesEndOfBlock(v, _, b)
}
/**
diff --git a/python/ql/src/semmle/python/essa/SsaCompute.qll b/python/ql/src/semmle/python/essa/SsaCompute.qll
index ad348675805..c9b4b28f96d 100644
--- a/python/ql/src/semmle/python/essa/SsaCompute.qll
+++ b/python/ql/src/semmle/python/essa/SsaCompute.qll
@@ -382,7 +382,9 @@ private module SsaComputeImpl {
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
private predicate blockPrecedesVar(SsaSourceVariable v, BasicBlock b) {
- varOccursInBlock(v, b.getASuccessor*())
+ varOccursInBlock(v, b)
+ or
+ SsaDefinitionsImpl::reachesEndOfBlock(v, _, _, b)
}
/**
From cc979d0b5f2d1467eaaad26493f0356af3177ce5 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 22 Sep 2020 09:36:33 +0200
Subject: [PATCH 060/185] C#: Add switch case expression type test
---
.../csharp8/switchCaseExprTypes.expected | 16 ++++++++++++++++
.../library-tests/csharp8/switchCaseExprTypes.ql | 4 ++++
2 files changed, 20 insertions(+)
create mode 100644 csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected
create mode 100644 csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql
diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected
new file mode 100644
index 00000000000..80d73991ea2
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.expected
@@ -0,0 +1,16 @@
+| patterns.cs:101:34:101:40 | "large" | Int32 |
+| patterns.cs:102:18:102:24 | "small" | Int32 |
+| patterns.cs:110:22:110:26 | (..., ...) | (Int32,Int32) |
+| patterns.cs:111:22:111:26 | (..., ...) | (Int32,Int32) |
+| patterns.cs:117:27:117:33 | (..., ...) | (Int32,Int32) |
+| patterns.cs:118:28:118:34 | (..., ...) | (Int32,Int32) |
+| patterns.cs:119:33:119:38 | (..., ...) | (Int32,Int32) |
+| patterns.cs:128:49:128:49 | 0 | MyStruct |
+| patterns.cs:129:38:129:38 | 1 | MyStruct |
+| patterns.cs:130:23:130:23 | 2 | MyStruct |
+| patterns.cs:131:27:131:27 | 3 | MyStruct |
+| patterns.cs:138:22:138:50 | throw ... | Object |
+| patterns.cs:139:22:139:22 | 3 | Object |
+| patterns.cs:140:42:140:42 | 4 | Object |
+| patterns.cs:141:29:141:29 | 5 | Object |
+| patterns.cs:142:41:142:41 | 6 | Object |
diff --git a/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql
new file mode 100644
index 00000000000..a9ad01da2f1
--- /dev/null
+++ b/csharp/ql/test/library-tests/csharp8/switchCaseExprTypes.ql
@@ -0,0 +1,4 @@
+import csharp
+
+from SwitchCaseExpr case
+select case.getBody(), case.getType().toString()
From c35a5d120a477e79fbbbd8c26e91fe21de7b4da1 Mon Sep 17 00:00:00 2001
From: Faten Healy <5361987+fatenhealy@users.noreply.github.com>
Date: Thu, 10 Sep 2020 16:28:02 +1000
Subject: [PATCH 061/185] C#: Increasing required size of RSA key to 2048
---
.../ql/src/Security Features/InsufficientKeySize.cs | 8 ++++----
.../src/Security Features/InsufficientKeySize.qhelp | 2 +-
.../ql/src/Security Features/InsufficientKeySize.ql | 8 ++++----
.../InsufficientKeySize/InsufficientKeySize.cs | 12 ++++++------
.../InsufficientKeySize/InsufficientKeySize.expected | 4 ++--
5 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.cs b/csharp/ql/src/Security Features/InsufficientKeySize.cs
index 5a12d01c1a1..9d12299dfb0 100644
--- a/csharp/ql/src/Security Features/InsufficientKeySize.cs
+++ b/csharp/ql/src/Security Features/InsufficientKeySize.cs
@@ -11,7 +11,7 @@ namespace InsufficientKeySize
{
try
{
- RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512); // BAD
+ RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024); // BAD
rsa.ImportParameters(key);
return rsa.Encrypt(plaintext, true);
}
@@ -27,7 +27,7 @@ namespace InsufficientKeySize
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); // BAD
- rsa = new RSACryptoServiceProvider(1024); // GOOD
+ rsa = new RSACryptoServiceProvider(2048); // GOOD
rsa.ImportParameters(key);
return rsa.Encrypt(plaintext, true);
}
@@ -58,7 +58,7 @@ namespace InsufficientKeySize
try
{
DSACryptoServiceProvider dsa = new DSACryptoServiceProvider(); // BAD
- dsa = new DSACryptoServiceProvider(1024); // GOOD
+ dsa = new DSACryptoServiceProvider(2048); // GOOD
dsa.ImportParameters(key);
return dsa.SignData(plaintext);
}
@@ -121,7 +121,7 @@ namespace InsufficientKeySize
try
{
// Create a new instance of DSACryptoServiceProvider.
- using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(1024)) // GOOD
+ using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider(2048)) // GOOD
{
// Import the key information.
DSA.ImportParameters(DSAKeyInfo);
diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
index 2b9ee39c610..906881cf0c2 100644
--- a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
+++ b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
@@ -8,7 +8,7 @@ are vulnerable to brute force attack when too small a key size is used.
-The key should be at least 1024-bit long when using RSA encryption, and 128-bit long when using
+
The key should be at least 2048-bit long when using RSA encryption, and 128-bit long when using
symmetric encryption.
diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql
index 08bdcfd6724..04623b1d4b0 100644
--- a/csharp/ql/src/Security Features/InsufficientKeySize.ql
+++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql
@@ -29,8 +29,8 @@ predicate incorrectUseOfDSA(ObjectCreation e, string msg) {
.getTarget()
.getDeclaringType()
.hasQualifiedName("System.Security.Cryptography", "DSACryptoServiceProvider") and
- exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and
- msg = "Key size should be at least 1024 bits for DSA encryption."
+ exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and
+ msg = "Key size should be at least 2048 bits for DSA encryption."
}
predicate incorrectUseOfRSA(ObjectCreation e, string msg) {
@@ -38,8 +38,8 @@ predicate incorrectUseOfRSA(ObjectCreation e, string msg) {
.getTarget()
.getDeclaringType()
.hasQualifiedName("System.Security.Cryptography", "RSACryptoServiceProvider") and
- exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 1024) and
- msg = "Key size should be at least 1024 bits for RSA encryption."
+ exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and
+ msg = "Key size should be at least 2048 bits for RSA encryption."
}
from Expr e, string msg
diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs
index 4ec825ddd63..90be31d0a07 100644
--- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs
+++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.cs
@@ -13,18 +13,18 @@ public class InsufficientKeySize
// GOOD: Key size is greater than 128
new RC2CryptoServiceProvider().EffectiveKeySize = 256;
- // BAD: Key size is less than 1024.
+ // BAD: Key size is less than 2048.
DSACryptoServiceProvider dsaBad = new DSACryptoServiceProvider(512);
- // GOOD: Key size defaults to 1024.
+ // GOOD: Key size defaults to 2048.
DSACryptoServiceProvider dsaGood1 = new DSACryptoServiceProvider();
- // GOOD: Key size is greater than 1024.
+ // GOOD: Key size is greater than 2048.
DSACryptoServiceProvider dsaGood2 = new DSACryptoServiceProvider(2048);
- // BAD: Key size is less than 1024.
+ // BAD: Key size is less than 2048.
RSACryptoServiceProvider rsaBad = new RSACryptoServiceProvider(512);
- // GOOD: Key size defaults to 1024.
+ // GOOD: Key size defaults to 2048.
RSACryptoServiceProvider rsaGood1 = new RSACryptoServiceProvider();
- // GOOD: Key size is greater than 1024.
+ // GOOD: Key size is greater than 2048.
RSACryptoServiceProvider rsaGood2 = new RSACryptoServiceProvider(2048);
}
}
diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected
index dc03302c7f3..feb87da77d2 100644
--- a/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected
+++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsufficientKeySize/InsufficientKeySize.expected
@@ -1,3 +1,3 @@
| InsufficientKeySize.cs:10:9:10:60 | ... = ... | Key size should be at least 128 bits for RC2 encryption. |
-| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 1024 bits for DSA encryption. |
-| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 1024 bits for RSA encryption. |
+| InsufficientKeySize.cs:17:43:17:75 | object creation of type DSACryptoServiceProvider | Key size should be at least 2048 bits for DSA encryption. |
+| InsufficientKeySize.cs:24:43:24:75 | object creation of type RSACryptoServiceProvider | Key size should be at least 2048 bits for RSA encryption. |
From bc09bc45bc79e3c1759a9e1598eb4f4a2fe2fa71 Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Tue, 22 Sep 2020 10:17:30 +0100
Subject: [PATCH 062/185] JS: Concatenate paths properly
---
javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
index d97dda5daf3..72933e1a7ad 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
@@ -1249,7 +1249,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set
cmd.addAll(xmlExtensions);
} else {
String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql";
- cmd.add(EnvironmentVariables.getCodeQLDist() + "/" + command);
+ cmd.add(Paths.get(EnvironmentVariables.getCodeQLDist(), command).toString());
cmd.add("database");
cmd.add("index-files");
cmd.add("--language");
From d34bd51f6167add2b022130280d8d27035c98f0d Mon Sep 17 00:00:00 2001
From: Asger Feldthaus
Date: Tue, 22 Sep 2020 10:28:40 +0100
Subject: [PATCH 063/185] JS: Call codeql.exe instead of codeql.cmd
---
javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
index 72933e1a7ad..edff3c8a857 100644
--- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
+++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java
@@ -1248,7 +1248,7 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set
cmd.add("--extensions");
cmd.addAll(xmlExtensions);
} else {
- String command = Env.getOS() == OS.WINDOWS ? "codeql.cmd" : "codeql";
+ String command = Env.getOS() == OS.WINDOWS ? "codeql.exe" : "codeql";
cmd.add(Paths.get(EnvironmentVariables.getCodeQLDist(), command).toString());
cmd.add("database");
cmd.add("index-files");
From 8de57c7d198b6b8b06b34adb1a23b9fcf7a55b64 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 4 Aug 2020 10:25:09 +0200
Subject: [PATCH 064/185] C#: Move ASP extraction from auto builder to
`pre-finalize.{sh,cmd}`
---
.../BuildScripts.cs | 69 +++++--------------
.../Semmle.Autobuild.CSharp/AspBuildRule.cs | 23 -------
.../CSharpAutobuilder.cs | 5 +-
.../Semmle.Autobuild.CSharp/XmlBuildRule.cs | 21 ------
.../AutobuildOptions.cs | 3 -
.../Semmle.Autobuild.Shared/Autobuilder.cs | 33 +++------
csharp/tools/autobuild.cmd | 3 -
csharp/tools/autobuild.sh | 4 --
csharp/tools/pre-finalize.cmd | 3 +
csharp/tools/pre-finalize.sh | 4 +-
10 files changed, 34 insertions(+), 134 deletions(-)
delete mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs
delete mode 100644 csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
index 51d56e6cda1..5934fb51c67 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
@@ -363,7 +363,6 @@ namespace Semmle.Autobuild.CSharp.Tests
string cwd = @"C:\Project")
{
string codeqlUpperLanguage = Language.CSharp.UpperCaseName;
- Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false";
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = "";
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = "";
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}";
@@ -400,8 +399,6 @@ namespace Semmle.Autobuild.CSharp.Tests
Actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0;
Actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0;
Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project\test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -419,7 +416,7 @@ namespace Semmle.Autobuild.CSharp.Tests
Actions.LoadXml[@"C:\Project\test.csproj"] = xml;
var autobuilder = CreateAutoBuilder(true);
- TestAutobuilderScript(autobuilder, 0, 6);
+ TestAutobuilderScript(autobuilder, 0, 4);
}
[Fact]
@@ -432,8 +429,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project/test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -451,7 +446,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.LoadXml[@"C:\Project/test.csproj"] = xml;
var autobuilder = CreateAutoBuilder(false);
- TestAutobuilderScript(autobuilder, 0, 7);
+ TestAutobuilderScript(autobuilder, 0, 5);
}
[Fact]
@@ -522,8 +517,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
public void TestLinuxBuildlessExtractionSuccess()
{
Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -531,7 +524,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.EnumerateDirectories[@"C:\Project"] = "";
var autobuilder = CreateAutoBuilder(false, buildless: "true");
- TestAutobuilderScript(autobuilder, 0, 3);
+ TestAutobuilderScript(autobuilder, 0, 1);
}
[Fact]
@@ -552,8 +545,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
public void TestLinuxBuildlessExtractionSolution()
{
Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -561,7 +552,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.EnumerateDirectories[@"C:\Project"] = "";
var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln");
- TestAutobuilderScript(autobuilder, 0, 3);
+ TestAutobuilderScript(autobuilder, 0, 1);
}
void SkipVsWhere()
@@ -598,8 +589,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess["dotnet --list-runtimes"] = 1;
Actions.RunProcessOut["dotnet --list-runtimes"] = "";
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -609,7 +598,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
SkipVsWhere();
var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests");
- TestAutobuilderScript(autobuilder, 0, 4);
+ TestAutobuilderScript(autobuilder, 0, 2);
}
[Fact]
@@ -624,12 +613,10 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcessOut["dotnet --list-runtimes"] = "";
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0;
Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build";
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(false);
- TestAutobuilderScript(autobuilder, 0, 5);
+ TestAutobuilderScript(autobuilder, 0, 3);
}
[Fact]
@@ -679,12 +666,10 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 0;
Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project";
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(true);
- TestAutobuilderScript(autobuilder, 0, 3);
+ TestAutobuilderScript(autobuilder, 0, 1);
}
[Fact]
@@ -729,8 +714,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln"] = 0;
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -752,7 +735,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1);
autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2);
- TestAutobuilderScript(autobuilder, 0, 6);
+ TestAutobuilderScript(autobuilder, 0, 4);
}
[Fact]
@@ -762,8 +745,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj"] = 0;
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project\test1.csproj"] = true;
Actions.FileExists[@"C:\Project\test2.csproj"] = true;
@@ -799,7 +780,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug",
vsToolsVersion: "12");
- TestAutobuilderScript(autobuilder, 0, 6);
+ TestAutobuilderScript(autobuilder, 0, 4);
}
[Fact]
@@ -834,8 +815,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
{
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -855,15 +834,13 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution1);
autobuilder.ProjectsOrSolutionsToBuild.Add(testSolution2);
- TestAutobuilderScript(autobuilder, 0, 4);
+ TestAutobuilderScript(autobuilder, 0, 2);
}
[Fact]
public void TestSkipNugetBuildless()
{
Actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -871,7 +848,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.EnumerateDirectories[@"C:\Project"] = "";
var autobuilder = CreateAutoBuilder(false, buildless: "true", solution: "foo.sln", nugetRestore: "false");
- TestAutobuilderScript(autobuilder, 0, 3);
+ TestAutobuilderScript(autobuilder, 0, 1);
}
@@ -885,8 +862,6 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore C:\Project/test.csproj"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project/test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -904,7 +879,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.LoadXml[@"C:\Project/test.csproj"] = xml;
var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now.
- TestAutobuilderScript(autobuilder, 0, 7);
+ TestAutobuilderScript(autobuilder, 0, 5);
}
[Fact]
@@ -922,8 +897,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists["test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -943,7 +916,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
- TestAutobuilderScript(autobuilder, 0, 11);
+ TestAutobuilderScript(autobuilder, 0, 9);
}
[Fact]
@@ -964,8 +937,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists["test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -985,7 +956,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
- TestAutobuilderScript(autobuilder, 0, 11);
+ TestAutobuilderScript(autobuilder, 0, 9);
}
[Fact]
@@ -999,8 +970,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0;
Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0;
Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project\test.csproj"] = true;
Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -1019,7 +988,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.LoadXml[@"C:\Project\test.csproj"] = xml;
var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3");
- TestAutobuilderScript(autobuilder, 0, 9);
+ TestAutobuilderScript(autobuilder, 0, 7);
}
[Fact]
@@ -1028,8 +997,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj"] = 0;
Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project\a\test.csproj"] = true;
Actions.FileExists[@"C:\Project\dirs.proj"] = true;
@@ -1065,7 +1032,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
var autobuilder = CreateAutoBuilder(true, msBuildArguments: "/P:Fu=Bar", msBuildTarget: "Windows", msBuildPlatform: "x86", msBuildConfiguration: "Debug",
vsToolsVersion: "12", allSolutions: "true");
- TestAutobuilderScript(autobuilder, 0, 5);
+ TestAutobuilderScript(autobuilder, 0, 3);
}
[Fact]
@@ -1074,8 +1041,6 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.RunProcess[@"nuget restore C:\Project/dirs.proj"] = 1;
Actions.RunProcess[@"mono C:\Project/.nuget/nuget.exe restore C:\Project/dirs.proj"] = 0;
Actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild C:\Project/dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0;
- Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0;
- Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0;
Actions.FileExists["csharp.log"] = true;
Actions.FileExists[@"C:\Project/a/test.csproj"] = true;
Actions.FileExists[@"C:\Project/dirs.proj"] = true;
@@ -1104,7 +1069,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.LoadXml[@"C:\Project/dirs.proj"] = dirsproj;
var autobuilder = CreateAutoBuilder(false);
- TestAutobuilderScript(autobuilder, 0, 5);
+ TestAutobuilderScript(autobuilder, 0, 3);
}
[Fact]
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs
deleted file mode 100644
index 2f69faeafde..00000000000
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/AspBuildRule.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Semmle.Autobuild.Shared;
-
-namespace Semmle.Autobuild.CSharp
-{
- ///
- /// ASP extraction.
- ///
- class AspBuildRule : IBuildRule
- {
- public BuildScript Analyse(Autobuilder builder, bool auto)
- {
- var javaHome = builder.JavaHome;
- var dist = builder.Distribution;
-
- var command = new CommandBuilder(builder.Actions).
- RunCommand(builder.Actions.PathCombine(javaHome, "bin", "java")).
- Argument("-jar").
- QuoteArgument(builder.Actions.PathCombine(dist, "tools", "extractor-asp.jar")).
- Argument(".");
- return command.Script;
- }
- }
-}
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
index 647a3ad2b4d..cf52b77b239 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
@@ -85,10 +85,7 @@ namespace Semmle.Autobuild.CSharp
break;
}
- return
- attempt &
- (() => new AspBuildRule().Analyse(this, false)) &
- (() => new XmlBuildRule().Analyse(this, false));
+ return attempt;
}
///
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs
deleted file mode 100644
index d262ec1f20b..00000000000
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/XmlBuildRule.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Semmle.Autobuild.Shared;
-
-namespace Semmle.Autobuild.CSharp
-{
- ///
- /// XML extraction.
- ///
- class XmlBuildRule : IBuildRule
- {
- public BuildScript Analyse(Autobuilder builder, bool auto)
- {
- if (!builder.Options.Indexing || builder.Odasa is null)
- return BuildScript.Success;
-
- var command = new CommandBuilder(builder.Actions).
- RunCommand(builder.Odasa).
- Argument("index --xml --extensions config csproj props xml");
- return command.Script;
- }
- }
-}
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
index 28c14be13ff..92bad4d6c7f 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
@@ -29,7 +29,6 @@ namespace Semmle.Autobuild.Shared
public readonly bool NugetRestore;
public readonly Language Language;
- public readonly bool Indexing;
///
/// Reads options from environment variables.
@@ -54,8 +53,6 @@ namespace Semmle.Autobuild.Shared
NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
Language = language;
-
- Indexing = !actions.GetEnvironmentVariable($"CODEQL_AUTOBUILDER_{Language.UpperCaseName}_NO_INDEXING").AsBool("no_indexing", false);
}
}
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
index 16ba41bfe34..1e8f58c45b7 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
@@ -1,4 +1,4 @@
-using Semmle.Util.Logging;
+using Semmle.Util.Logging;
using System;
using System.Collections.Generic;
using System.IO;
@@ -190,21 +190,10 @@ namespace Semmle.Autobuild.Shared
});
CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT");
- SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST");
SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS");
CodeQlPlatform = Actions.GetEnvironmentVariable("CODEQL_PLATFORM");
- JavaHome =
- Actions.GetEnvironmentVariable("CODEQL_JAVA_HOME") ??
- Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME") ??
- throw new InvalidEnvironmentException("The environment variable CODEQL_JAVA_HOME or SEMMLE_JAVA_HOME has not been set.");
-
- Distribution =
- CodeQLExtractorLangRoot ??
- SemmleDist ??
- throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT or SEMMLE_DIST has not been set.");
-
TrapDir =
Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ??
Actions.GetEnvironmentVariable("TRAP_FOLDER") ??
@@ -275,15 +264,6 @@ namespace Semmle.Autobuild.Shared
///
public string? CodeQLExtractorLangRoot { get; }
- ///
- /// Value of SEMMLE_DIST environment variable.
- ///
- private string? SemmleDist { get; }
-
- public string Distribution { get; }
-
- public string JavaHome { get; }
-
///
/// Value of SEMMLE_PLATFORM_TOOLS environment variable.
///
@@ -298,13 +278,20 @@ namespace Semmle.Autobuild.Shared
/// The absolute path of the odasa executable.
/// null if we are running in CodeQL.
///
- public string? Odasa => SemmleDist is null ? null : Actions.PathCombine(SemmleDist, "tools", "odasa");
+ public string? Odasa
+ {
+ get
+ {
+ var semmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST");
+ return semmleDist is null ? null : Actions.PathCombine(semmleDist, "tools", "odasa");
+ }
+ }
///
/// Construct a command that executed the given wrapped in
/// an odasa --index, unless indexing has been disabled, in which case
/// is run directly.
///
- public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing && !(Odasa is null) ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd);
+ public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Odasa is null ? builder.RunCommand(cmd) : builder.IndexCommand(Odasa, cmd);
}
}
diff --git a/csharp/tools/autobuild.cmd b/csharp/tools/autobuild.cmd
index 418ba37a972..7d24060ff46 100644
--- a/csharp/tools/autobuild.cmd
+++ b/csharp/tools/autobuild.cmd
@@ -1,7 +1,4 @@
@echo off
-rem The autobuilder is already being traced
-set CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING=true
-
type NUL && "%CODEQL_EXTRACTOR_CSHARP_ROOT%/tools/%CODEQL_PLATFORM%/Semmle.Autobuild.CSharp.exe"
exit /b %ERRORLEVEL%
diff --git a/csharp/tools/autobuild.sh b/csharp/tools/autobuild.sh
index 341f64af50b..e8e007a10d7 100755
--- a/csharp/tools/autobuild.sh
+++ b/csharp/tools/autobuild.sh
@@ -7,8 +7,4 @@ if [ "$CODEQL_PLATFORM" != "linux64" ] && [ "$CODEQL_PLATFORM" != "osx64" ] ; th
exit 1
fi
-# The autobuilder is already being traced
-CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING="true"
-export CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING
-
"$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/$CODEQL_PLATFORM/Semmle.Autobuild.CSharp" || exit $?
diff --git a/csharp/tools/pre-finalize.cmd b/csharp/tools/pre-finalize.cmd
index a8a5a6bc68a..b6e7abab41b 100644
--- a/csharp/tools/pre-finalize.cmd
+++ b/csharp/tools/pre-finalize.cmd
@@ -9,4 +9,7 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^
--language xml ^
-- ^
"%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%"
+IF %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
+
+type NUL && "%CODEQL_JAVA_HOME%\bin\java.exe" -jar "%CODEQL_EXTRACTOR_CSHARP_ROOT%\tools\extractor-asp.jar" .
exit /b %ERRORLEVEL%
diff --git a/csharp/tools/pre-finalize.sh b/csharp/tools/pre-finalize.sh
index ed12b3b1d72..0e8ab78c9fd 100755
--- a/csharp/tools/pre-finalize.sh
+++ b/csharp/tools/pre-finalize.sh
@@ -10,4 +10,6 @@ set -eu
--size-limit 10m \
--language xml \
-- \
- "$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE"
\ No newline at end of file
+ "$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE"
+
+"$CODEQL_JAVA_HOME/bin/java" -jar "$CODEQL_EXTRACTOR_CSHARP_ROOT/tools/extractor-asp.jar" .
From a89d13a5eef8f33d79161987639bf4b8d4c460f6 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 22 Sep 2020 11:20:22 +0200
Subject: [PATCH 065/185] C#: Add change notes for increased required key size
in 'cs/insufficient-key-size'
---
change-notes/1.26/analysis-csharp.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/change-notes/1.26/analysis-csharp.md b/change-notes/1.26/analysis-csharp.md
index 5b65481c925..3d17e00ab70 100644
--- a/change-notes/1.26/analysis-csharp.md
+++ b/change-notes/1.26/analysis-csharp.md
@@ -12,7 +12,7 @@ The following changes in version 1.26 affect C# analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
-
+| Weak encryption: Insufficient key size (`cs/insufficient-key-size`) | More results | The required key size has been increased from 1024 to 2048. |
## Removal of old queries
From 5f96c37b283a956e94ddf0960f564800ada9f5d9 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 22 Sep 2020 09:40:07 +0200
Subject: [PATCH 066/185] C#: Fix switch case expression types
---
.../Entities/Expressions/Switch.cs | 9 ++++----
.../controlflow/guards/Implications.expected | 6 +++++
.../csharp8/switchCaseExprTypes.expected | 22 +++++++++----------
3 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs
index 5316998a38f..0dfd33751fc 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs
@@ -18,10 +18,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected override void PopulateExpression(TextWriter trapFile)
{
SwitchedExpr = Expression.Create(cx, Syntax.GoverningExpression, this, -1);
- int child = 0;
- foreach (var arm in Syntax.Arms)
+ for (var i = 0; i < Syntax.Arms.Count; i++)
{
- new SwitchCase(cx, arm, this, child++);
+ new SwitchCase(cx, Syntax.Arms[i], this, i);
}
}
}
@@ -29,7 +28,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
class SwitchCase : Expression
{
internal SwitchCase(Context cx, SwitchExpressionArmSyntax arm, Switch parent, int child) :
- base(new ExpressionInfo(cx, parent.SwitchedExpr.Type, cx.Create(arm.GetLocation()), ExprKind.SWITCH_CASE, parent, child, false, null))
+ base(new ExpressionInfo(
+ cx, Entities.Type.Create(cx, cx.GetType(arm.Expression)), cx.Create(arm.GetLocation()),
+ ExprKind.SWITCH_CASE, parent, child, false, null))
{
cx.CreatePattern(arm.Pattern, this, 0);
if (arm.WhenClause is WhenClauseSyntax when)
diff --git a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected
index 59f89c30800..ec6c60ff9e8 100644
--- a/csharp/ql/test/library-tests/controlflow/guards/Implications.expected
+++ b/csharp/ql/test/library-tests/controlflow/guards/Implications.expected
@@ -397,8 +397,14 @@
| Guards.cs:276:16:276:16 | access to parameter o | match access to type Action