From 799d509f26d0954025fea5302945387194fdef46 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 17:31:41 +0100 Subject: [PATCH 001/550] Upload LDAP Injection query, qhelp and tests --- .../src/Security/CWE-090/LDAPInjection.qhelp | 32 +++++++ .../ql/src/Security/CWE-090/LDAPInjection.ql | 86 +++++++++++++++++++ .../Security/CWE-090/tests/ldap3_sanitized.py | 58 +++++++++++++ .../CWE-090/tests/ldap3_sanitized_asObj.py | 57 ++++++++++++ .../CWE-090/tests/ldap3_unsanitized.py | 56 ++++++++++++ .../CWE-090/tests/ldap3_unsanitized_asObj.py | 55 ++++++++++++ .../Security/CWE-090/tests/ldap_sanitized.py | 56 ++++++++++++ .../CWE-090/tests/ldap_sanitized_asObj.py | 57 ++++++++++++ .../CWE-090/tests/ldap_unsanitized.py | 52 +++++++++++ .../CWE-090/tests/ldap_unsanitized_asObj.py | 52 +++++++++++ 10 files changed, 561 insertions(+) create mode 100644 python/ql/src/Security/CWE-090/LDAPInjection.qhelp create mode 100644 python/ql/src/Security/CWE-090/LDAPInjection.ql create mode 100644 python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap_sanitized.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py create mode 100644 python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py diff --git a/python/ql/src/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/Security/CWE-090/LDAPInjection.qhelp new file mode 100644 index 00000000000..e077ccc3afe --- /dev/null +++ b/python/ql/src/Security/CWE-090/LDAPInjection.qhelp @@ -0,0 +1,32 @@ + + + + +

If an LDAP query is built by a not sanitized user-provided value, a user is likely to be able to run malicious LDAP queries.

+
+ + +

In case user input must compose an LDAP query, it should be escaped in order to avoid a malicious user supplying special characters that change the actual purpose of the query. To do so, functions that ldap frameworks provide such as escape_filter_chars should be applied to that user input. + + + + +

  • + OWASP + LDAP Injection +
  • +
  • + SonarSource + RSPEC-2078 +
  • +
  • + Python + LDAP Documentation +
  • +
  • + CWE- + 090 +
  • + + +
    \ No newline at end of file diff --git a/python/ql/src/Security/CWE-090/LDAPInjection.ql b/python/ql/src/Security/CWE-090/LDAPInjection.ql new file mode 100644 index 00000000000..d6ff1ad5607 --- /dev/null +++ b/python/ql/src/Security/CWE-090/LDAPInjection.ql @@ -0,0 +1,86 @@ +/** + * @name Python LDAP Injection + * @description Python LDAP Injection through search filter + * @kind path-problem + * @problem.severity error + * @id python/ldap-injection + * @tags experimental + * security + * external/cwe/cwe-090 + */ + +import python +import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.internal.TaintTrackingPublic +import DataFlow::PathGraph + +class InitializeSink extends DataFlow::Node { + InitializeSink() { + exists(SsaVariable initVar, CallNode searchCall | + // get variable whose value equals a call to ldap.initialize + initVar.getDefinition().getImmediateDominator() = Value::named("ldap.initialize").getACall() and + // get the Call in which the previous variable is used + initVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and + // restrict that call's attribute (something.this) to match %search% + searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and + // set the third argument (search_filter) as sink + this.asExpr() = searchCall.getArg(2).getNode() + // set the first argument (DN) as sink + // or this.asExpr() = searchCall.getArg(0) // Should this be set? + ) + } +} + +class ConnectionSink extends DataFlow::Node { + ConnectionSink() { + exists(SsaVariable connVar, CallNode searchCall | + // get variable whose value equals a call to ldap.initialize + connVar.getDefinition().getImmediateDominator() = Value::named("ldap3.Connection").getACall() and + // get the Call in which the previous variable is used + connVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and + // restrict that call's attribute (something.this) to match %search% + searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and + // set the second argument (search_filter) as sink + this.asExpr() = searchCall.getArg(1).getNode() + // set the first argument (DN) as sink + // or this.asExpr() = searchCall.getArg(0) // Should this be set? + ) + } +} + +class EscapeSanitizer extends DataFlow::Node { + EscapeSanitizer() { + exists(Call c | + ( + // avoid flow through any %escape% function + c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() + c.getFunc().(Name).getId().matches("%escape%") // %escape%() + ) and + this.asExpr() = c + ) + } +} + +class LDAPInjectionSink extends DataFlow::Node { + LDAPInjectionSink() { + this instanceof InitializeSink or + this instanceof ConnectionSink + } +} + +class LDAPInjectionFlowConfig extends TaintTracking::Configuration { + LDAPInjectionFlowConfig() { this = "LDAPInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof LDAPInjectionSink } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } +} + +from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ LDAP query executes $@.", sink.getNode(), "This", + source.getNode(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py b/python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py new file mode 100644 index 00000000000..05bc1e24f71 --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py @@ -0,0 +1,58 @@ +import ldap3 +from ldap3.utils.conv import escape_filter_chars +from flask import request, Flask + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + sanitized_dn = "dc=%s" % request.args['dc'] + sanitized_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars( + request.args['username']) + + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user=sanitized_dn, auto_bind=True) + conn.search(sanitized_dn, sanitized_filter) + return conn.response + + +@app.route("/var_tainted") +def var_tainted(): + sanitized_dn = request.args['dc'] + sanitized_filter = request.args['username'] + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars(sanitized_filter) + + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user=dn, auto_bind=True) + conn.search(dn, search_filter) + return conn.response + + +@app.route("/direct") +def direct(): + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user="dc=%s" % + request.args['dc'], auto_bind=True) + conn.search("dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % + escape_filter_chars(request.args['username'])) + return conn.response + + +@ app.route("/with_") +def with_(): + sanitized_dn = request.args['dc'] + sanitized_filter = escape_filter_chars(request.args['username']) + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + srv = ldap3.Server('localhost', port=1337) + with ldap3.Connection(server, auto_bind=True) as conn: + conn.search(dn, search_filter) + return conn.response + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py b/python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py new file mode 100644 index 00000000000..c7dbb345058 --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py @@ -0,0 +1,57 @@ +from ldap3 import Server, Connection +from ldap3.utils.conv import escape_filter_chars +from flask import request, Flask + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + sanitized_dn = "dc=%s" % request.args['dc'] + sanitized_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars( + request.args['username']) + + srv = Server('localhost', port=1337) + conn = Connection(srv, user=sanitized_dn, auto_bind=True) + conn.search(sanitized_dn, sanitized_filter) + return conn.response + + +@app.route("/var_tainted") +def var_tainted(): + sanitized_dn = request.args['dc'] + sanitized_filter = request.args['username'] + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars(sanitized_filter) + + srv = Server('localhost', port=1337) + conn = Connection(srv, user=dn, auto_bind=True) + conn.search(dn, search_filter) + return conn.response + + +@app.route("/direct") +def direct(): + srv = Server('localhost', port=1337) + conn = Connection(srv, user="dc=%s" % request.args['dc'], auto_bind=True) + conn.search("dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % + escape_filter_chars(request.args['username'])) + return conn.response + + +@app.route("/with_2") +def with_2(): + sanitized_dn = request.args['dc'] + sanitized_filter = escape_filter_chars(request.args['username']) + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + srv = Server('localhost', port=1337) + with Connection(server, auto_bind=True) as conn: + conn.search(dn, search_filter) + return conn.response + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py b/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py new file mode 100644 index 00000000000..fcd00c0269c --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py @@ -0,0 +1,56 @@ +import ldap3 +from flask import request, Flask + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + unsanitized_dn = "dc=%s" % request.args['dc'] + unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] + + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user=unsanitized_dn, auto_bind=True) + conn.search(unsanitized_dn, unsanitized_filter) + return conn.response + + +@app.route("/var_tainted") +def var_tainted(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user=dn, auto_bind=True) + conn.search(dn, search_filter) + return conn.response + + +@app.route("/direct") +def direct(): + srv = ldap3.Server('localhost', port=1337) + conn = ldap3.Connection(srv, user="dc=%s" % + request.args['dc'], auto_bind=True) + conn.search("dc=%s" % unsanitized_dn, + "(&(objectClass=*)(uid=%s))" % request.args['username']) + return conn.response + + +@app.route("/with_") +def with_(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + srv = ldap3.Server('localhost', port=1337) + with ldap3.Connection(server, auto_bind=True) as conn: + conn.search(dn, search_filter) + return conn.response + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py b/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py new file mode 100644 index 00000000000..634ee992f3e --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py @@ -0,0 +1,55 @@ +from ldap3 import Server, Connection +from flask import request, Flask + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + unsanitized_dn = "dc=%s" % request.args['dc'] + unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] + + srv = Server('localhost', port=1337) + conn = Connection(srv, user=unsanitized_dn, auto_bind=True) + conn.search(unsanitized_dn, unsanitized_filter) + return conn.response + + +@app.route("/var_tainted") +def var_tainted(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + srv = Server('localhost', port=1337) + conn = Connection(srv, user=dn, auto_bind=True) + conn.search(dn, search_filter) + return conn.response + + +@app.route("/direct") +def direct(): + srv = Server('localhost', port=1337) + conn = Connection(srv, user="dc=%s" % request.args['dc'], auto_bind=True) + conn.search( + "dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % request.args['username']) + return conn.response + + +@app.route("/with_2") +def with_2(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + srv = Server('localhost', port=1337) + with Connection(server, auto_bind=True) as conn: + conn.search(dn, search_filter) + return conn.response + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap_sanitized.py b/python/ql/src/Security/CWE-090/tests/ldap_sanitized.py new file mode 100644 index 00000000000..83bafa79195 --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap_sanitized.py @@ -0,0 +1,56 @@ +from flask import request, Flask +import ldap +import ldap.filter +import ldap.dn + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + sanitized_dn = "dc=%s" % ldap.dn.escape_dn_chars(request.args['dc']) + sanitized_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars( + request.args['username']) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + sanitized_dn, ldap.SCOPE_SUBTREE, sanitized_filter) + return user[0] + + +@app.route("/var_tainted") +def var_tainted(): + sanitized_dn = request.args['dc'] + sanitized_filter = request.args['username'] + + dn = "dc=%s" % ldap.dn.escape_dn_chars(sanitized_dn) + search_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(sanitized_filter) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + + +@app.route("/direct") +def direct(): + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s("dc=%s" % ldap.dn.escape_dn_chars( + request.args['dc']), ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(request.args['username'])) + return user[0] + + +@app.route("/with_") +def with_(): + sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) + sanitized_filter = ldap.filter.escape_filter_chars( + request.args['username']) + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + with ldap.initialize("ldap://127.0.0.1:1337") as ldap_connection: + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py b/python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py new file mode 100644 index 00000000000..1c9662a4bcf --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py @@ -0,0 +1,57 @@ +from flask import request, Flask +from ldap import initialize +import ldap.filter +import ldap.dn + +app = Flask(__name__) + + +@app.route("/tainted_var_2") +def tainted_var_2(): + sanitized_dn = "dc=%s" % ldap.dn.escape_dn_chars(request.args['dc']) + sanitized_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars( + request.args['username']) + + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + sanitized_dn, ldap.SCOPE_SUBTREE, sanitized_filter) + return user[0] + + +@app.route("/var_tainted_2") +def var_tainted_2(): + sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) + sanitized_filter = ldap.filter.escape_filter_chars( + request.args['username']) + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + + +@app.route("/direct_2") +def direct_2(): + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s("dc=%s" % ldap.dn.escape_dn_chars( + request.args['dc']), ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(request.args['username'])) + return user[0] + + +@app.route("/with_2") +def with_2(): + sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) + sanitized_filter = ldap.filter.escape_filter_chars( + request.args['username']) + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + with initialize("ldap://127.0.0.1:1337") as ldap_connection: + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py b/python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py new file mode 100644 index 00000000000..8b22596b578 --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py @@ -0,0 +1,52 @@ +from flask import request, Flask +import ldap + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + unsanitized_dn = "dc=%s" % request.args['dc'] + unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsanitized_dn, ldap.SCOPE_SUBTREE, unsanitized_filter) + return user[0] + + +@app.route("/var_tainted") +def var_tainted(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + + +@app.route("/direct") +def direct(): + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + "dc=%s" % request.args['dc'], ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % request.args['username']) + return user[0] + + +@app.route("/with_") +def with_(): + sanitized_dn = request.args['dc'] + sanitized_filter = request.args['username'] + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + with ldap.initialize("ldap://127.0.0.1:1337") as ldap_connection: + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py b/python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py new file mode 100644 index 00000000000..49774dda2ec --- /dev/null +++ b/python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py @@ -0,0 +1,52 @@ +from flask import request, Flask +from ldap import initialize + +app = Flask(__name__) + + +@app.route("/tainted_var") +def tainted_var(): + unsanitized_dn = "dc=%s" % request.args['dc'] + unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] + + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsanitized_dn, ldap.SCOPE_SUBTREE, unsanitized_filter) + return user[0] + + +@app.route("/var_tainted") +def var_tainted(): + unsanitized_dn = request.args['dc'] + unsanitized_filter = request.args['username'] + + dn = "dc=%s" % unsanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter + + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + + +@app.route("/direct") +def direct(): + ldap_connection = initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + "dc=%s" % request.args['dc'], ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % request.args['username']) + return user[0] + + +@app.route("/with_2") +def with_2(): + sanitized_dn = request.args['dc'] + sanitized_filter = request.args['username'] + + dn = "dc=%s" % sanitized_dn + search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter + + with initialize("ldap://127.0.0.1:1337") as ldap_connection: + user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) + return user[0] + +# if __name__ == "__main__": +# app.run(debug=True) From 719b48cbaf8193f57560bd499e890f679c8d7931 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:21:47 +0100 Subject: [PATCH 002/550] Move to experimental folder --- .../src/{ => experimental}/Security/CWE-090/LDAPInjection.qhelp | 0 .../ql/src/{ => experimental}/Security/CWE-090/LDAPInjection.ql | 0 .../{ => experimental}/Security/CWE-090/tests/ldap3_sanitized.py | 0 .../Security/CWE-090/tests/ldap3_sanitized_asObj.py | 0 .../Security/CWE-090/tests/ldap3_unsanitized.py | 0 .../Security/CWE-090/tests/ldap3_unsanitized_asObj.py | 0 .../{ => experimental}/Security/CWE-090/tests/ldap_sanitized.py | 0 .../Security/CWE-090/tests/ldap_sanitized_asObj.py | 0 .../{ => experimental}/Security/CWE-090/tests/ldap_unsanitized.py | 0 .../Security/CWE-090/tests/ldap_unsanitized_asObj.py | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/{ => experimental}/Security/CWE-090/LDAPInjection.qhelp (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/LDAPInjection.ql (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap3_sanitized.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap3_sanitized_asObj.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap3_unsanitized.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap3_unsanitized_asObj.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap_sanitized.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap_sanitized_asObj.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap_unsanitized.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-090/tests/ldap_unsanitized_asObj.py (100%) diff --git a/python/ql/src/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp similarity index 100% rename from python/ql/src/Security/CWE-090/LDAPInjection.qhelp rename to python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp diff --git a/python/ql/src/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql similarity index 100% rename from python/ql/src/Security/CWE-090/LDAPInjection.ql rename to python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap3_sanitized.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap3_sanitized_asObj.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap3_unsanitized.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap3_unsanitized_asObj.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap_sanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap_sanitized.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap_sanitized_asObj.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap_unsanitized.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py diff --git a/python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py similarity index 100% rename from python/ql/src/Security/CWE-090/tests/ldap_unsanitized_asObj.py rename to python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py From 95a1dae3154367e47fcaaa97cc35dfdb9b1c1fcb Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:36:38 +0100 Subject: [PATCH 003/550] Precision warn and Remove CWE reference --- .../ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp | 4 ---- python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp index e077ccc3afe..a570549abfc 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp @@ -23,10 +23,6 @@ Python LDAP Documentation -
  • - CWE- - 090 -
  • \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index d6ff1ad5607..55e477be50a 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -9,6 +9,7 @@ * external/cwe/cwe-090 */ +// Determine precision above import python import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.DataFlow From 85ec82a389a76ef721d879e7c294550bb56fe8ac Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 21:07:08 +0200 Subject: [PATCH 004/550] Refactor in progress --- .../Security/CWE-090/LDAPInjection.ql | 82 +++---------------- .../ldap3_unsanitized.py => ldap3_bad.py} | 0 .../ldap3_sanitized.py => ldap3_good.py} | 0 .../CWE-090/tests/ldap3_sanitized_asObj.py | 57 ------------- .../CWE-090/tests/ldap3_unsanitized_asObj.py | 55 ------------- .../Security/CWE-090/tests/ldap_sanitized.py | 56 ------------- .../CWE-090/tests/ldap_sanitized_asObj.py | 57 ------------- .../CWE-090/tests/ldap_unsanitized.py | 52 ------------ .../CWE-090/tests/ldap_unsanitized_asObj.py | 52 ------------ .../Security/CWE-090/unit_tests/ldap_bad.py | 46 +++++++++++ .../Security/CWE-090/unit_tests/ldap_good.py | 60 ++++++++++++++ .../experimental/semmle/python/Concepts.qll | 43 ++++++++++ .../semmle/python/frameworks/Stdlib.qll | 56 +++++++++++++ .../security/injection/LDAPInjection.qll | 21 +++++ 14 files changed, 236 insertions(+), 401 deletions(-) rename python/ql/src/experimental/Security/CWE-090/{tests/ldap3_unsanitized.py => ldap3_bad.py} (100%) rename python/ql/src/experimental/Security/CWE-090/{tests/ldap3_sanitized.py => ldap3_good.py} (100%) delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py create mode 100644 python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py create mode 100644 python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py create mode 100644 python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 55e477be50a..778b771f883 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -11,77 +11,15 @@ // Determine precision above import python -import semmle.python.dataflow.new.RemoteFlowSources -import semmle.python.dataflow.new.DataFlow -import semmle.python.dataflow.new.TaintTracking -import semmle.python.dataflow.new.internal.TaintTrackingPublic +import experimental.semmle.python.security.injection.LDAPInjection import DataFlow::PathGraph -class InitializeSink extends DataFlow::Node { - InitializeSink() { - exists(SsaVariable initVar, CallNode searchCall | - // get variable whose value equals a call to ldap.initialize - initVar.getDefinition().getImmediateDominator() = Value::named("ldap.initialize").getACall() and - // get the Call in which the previous variable is used - initVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and - // restrict that call's attribute (something.this) to match %search% - searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and - // set the third argument (search_filter) as sink - this.asExpr() = searchCall.getArg(2).getNode() - // set the first argument (DN) as sink - // or this.asExpr() = searchCall.getArg(0) // Should this be set? - ) - } -} - -class ConnectionSink extends DataFlow::Node { - ConnectionSink() { - exists(SsaVariable connVar, CallNode searchCall | - // get variable whose value equals a call to ldap.initialize - connVar.getDefinition().getImmediateDominator() = Value::named("ldap3.Connection").getACall() and - // get the Call in which the previous variable is used - connVar.getAUse().getNode() = searchCall.getNode().getFunc().(Attribute).getObject() and - // restrict that call's attribute (something.this) to match %search% - searchCall.getNode().getFunc().(Attribute).getName().matches("%search%") and - // set the second argument (search_filter) as sink - this.asExpr() = searchCall.getArg(1).getNode() - // set the first argument (DN) as sink - // or this.asExpr() = searchCall.getArg(0) // Should this be set? - ) - } -} - -class EscapeSanitizer extends DataFlow::Node { - EscapeSanitizer() { - exists(Call c | - ( - // avoid flow through any %escape% function - c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() - c.getFunc().(Name).getId().matches("%escape%") // %escape%() - ) and - this.asExpr() = c - ) - } -} - -class LDAPInjectionSink extends DataFlow::Node { - LDAPInjectionSink() { - this instanceof InitializeSink or - this instanceof ConnectionSink - } -} - -class LDAPInjectionFlowConfig extends TaintTracking::Configuration { - LDAPInjectionFlowConfig() { this = "LDAPInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof LDAPInjectionSink } - - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } -} - -from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ LDAP query executes $@.", sink.getNode(), "This", - source.getNode(), "a user-provided value" +from + LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + LDAPQuery castedSink +where + config.hasFlowPath(source, sink) and + castedSink = sink.getNode() +select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@ probably leaking $@.", + sink.getNode(), "This", source.getNode(), "a user-provided value", castedSink.getLDAPNode(), + castedSink.getLDAPPart(), castedSink.getAttrList(), "this attribute(s)" diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized.py b/python/ql/src/experimental/Security/CWE-090/ldap3_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized.py rename to python/ql/src/experimental/Security/CWE-090/ldap3_bad.py diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized.py b/python/ql/src/experimental/Security/CWE-090/ldap3_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized.py rename to python/ql/src/experimental/Security/CWE-090/ldap3_good.py diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py deleted file mode 100644 index c7dbb345058..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_sanitized_asObj.py +++ /dev/null @@ -1,57 +0,0 @@ -from ldap3 import Server, Connection -from ldap3.utils.conv import escape_filter_chars -from flask import request, Flask - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - sanitized_dn = "dc=%s" % request.args['dc'] - sanitized_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars( - request.args['username']) - - srv = Server('localhost', port=1337) - conn = Connection(srv, user=sanitized_dn, auto_bind=True) - conn.search(sanitized_dn, sanitized_filter) - return conn.response - - -@app.route("/var_tainted") -def var_tainted(): - sanitized_dn = request.args['dc'] - sanitized_filter = request.args['username'] - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars(sanitized_filter) - - srv = Server('localhost', port=1337) - conn = Connection(srv, user=dn, auto_bind=True) - conn.search(dn, search_filter) - return conn.response - - -@app.route("/direct") -def direct(): - srv = Server('localhost', port=1337) - conn = Connection(srv, user="dc=%s" % request.args['dc'], auto_bind=True) - conn.search("dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % - escape_filter_chars(request.args['username'])) - return conn.response - - -@app.route("/with_2") -def with_2(): - sanitized_dn = request.args['dc'] - sanitized_filter = escape_filter_chars(request.args['username']) - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - srv = Server('localhost', port=1337) - with Connection(server, auto_bind=True) as conn: - conn.search(dn, search_filter) - return conn.response - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py deleted file mode 100644 index 634ee992f3e..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap3_unsanitized_asObj.py +++ /dev/null @@ -1,55 +0,0 @@ -from ldap3 import Server, Connection -from flask import request, Flask - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - unsanitized_dn = "dc=%s" % request.args['dc'] - unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] - - srv = Server('localhost', port=1337) - conn = Connection(srv, user=unsanitized_dn, auto_bind=True) - conn.search(unsanitized_dn, unsanitized_filter) - return conn.response - - -@app.route("/var_tainted") -def var_tainted(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - srv = Server('localhost', port=1337) - conn = Connection(srv, user=dn, auto_bind=True) - conn.search(dn, search_filter) - return conn.response - - -@app.route("/direct") -def direct(): - srv = Server('localhost', port=1337) - conn = Connection(srv, user="dc=%s" % request.args['dc'], auto_bind=True) - conn.search( - "dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % request.args['username']) - return conn.response - - -@app.route("/with_2") -def with_2(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - srv = Server('localhost', port=1337) - with Connection(server, auto_bind=True) as conn: - conn.search(dn, search_filter) - return conn.response - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py deleted file mode 100644 index 83bafa79195..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized.py +++ /dev/null @@ -1,56 +0,0 @@ -from flask import request, Flask -import ldap -import ldap.filter -import ldap.dn - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - sanitized_dn = "dc=%s" % ldap.dn.escape_dn_chars(request.args['dc']) - sanitized_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars( - request.args['username']) - - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - sanitized_dn, ldap.SCOPE_SUBTREE, sanitized_filter) - return user[0] - - -@app.route("/var_tainted") -def var_tainted(): - sanitized_dn = request.args['dc'] - sanitized_filter = request.args['username'] - - dn = "dc=%s" % ldap.dn.escape_dn_chars(sanitized_dn) - search_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(sanitized_filter) - - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - - -@app.route("/direct") -def direct(): - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s("dc=%s" % ldap.dn.escape_dn_chars( - request.args['dc']), ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(request.args['username'])) - return user[0] - - -@app.route("/with_") -def with_(): - sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) - sanitized_filter = ldap.filter.escape_filter_chars( - request.args['username']) - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - with ldap.initialize("ldap://127.0.0.1:1337") as ldap_connection: - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py deleted file mode 100644 index 1c9662a4bcf..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap_sanitized_asObj.py +++ /dev/null @@ -1,57 +0,0 @@ -from flask import request, Flask -from ldap import initialize -import ldap.filter -import ldap.dn - -app = Flask(__name__) - - -@app.route("/tainted_var_2") -def tainted_var_2(): - sanitized_dn = "dc=%s" % ldap.dn.escape_dn_chars(request.args['dc']) - sanitized_filter = "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars( - request.args['username']) - - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - sanitized_dn, ldap.SCOPE_SUBTREE, sanitized_filter) - return user[0] - - -@app.route("/var_tainted_2") -def var_tainted_2(): - sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) - sanitized_filter = ldap.filter.escape_filter_chars( - request.args['username']) - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - - -@app.route("/direct_2") -def direct_2(): - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s("dc=%s" % ldap.dn.escape_dn_chars( - request.args['dc']), ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % ldap.filter.escape_filter_chars(request.args['username'])) - return user[0] - - -@app.route("/with_2") -def with_2(): - sanitized_dn = ldap.dn.escape_dn_chars(request.args['dc']) - sanitized_filter = ldap.filter.escape_filter_chars( - request.args['username']) - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - with initialize("ldap://127.0.0.1:1337") as ldap_connection: - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py deleted file mode 100644 index 8b22596b578..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized.py +++ /dev/null @@ -1,52 +0,0 @@ -from flask import request, Flask -import ldap - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - unsanitized_dn = "dc=%s" % request.args['dc'] - unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] - - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - unsanitized_dn, ldap.SCOPE_SUBTREE, unsanitized_filter) - return user[0] - - -@app.route("/var_tainted") -def var_tainted(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - - -@app.route("/direct") -def direct(): - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - "dc=%s" % request.args['dc'], ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % request.args['username']) - return user[0] - - -@app.route("/with_") -def with_(): - sanitized_dn = request.args['dc'] - sanitized_filter = request.args['username'] - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - with ldap.initialize("ldap://127.0.0.1:1337") as ldap_connection: - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py b/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py deleted file mode 100644 index 49774dda2ec..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/tests/ldap_unsanitized_asObj.py +++ /dev/null @@ -1,52 +0,0 @@ -from flask import request, Flask -from ldap import initialize - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - unsanitized_dn = "dc=%s" % request.args['dc'] - unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] - - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - unsanitized_dn, ldap.SCOPE_SUBTREE, unsanitized_filter) - return user[0] - - -@app.route("/var_tainted") -def var_tainted(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - - -@app.route("/direct") -def direct(): - ldap_connection = initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - "dc=%s" % request.args['dc'], ldap.SCOPE_SUBTREE, "(&(objectClass=*)(uid=%s))" % request.args['username']) - return user[0] - - -@app.route("/with_2") -def with_2(): - sanitized_dn = request.args['dc'] - sanitized_filter = request.args['username'] - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - with initialize("ldap://127.0.0.1:1337") as ldap_connection: - user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) - return user[0] - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py new file mode 100644 index 00000000000..a409a38e019 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py @@ -0,0 +1,46 @@ +from flask import request, Flask +import ldap + +app = Flask(__name__) + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter, ["testAttr1", "testAttr2"]) + + +@app.route("/direct") +def direct(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + user = ldap.initialize("ldap://127.0.0.1:1337").search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter, ["testAttr1", "testAttr2"]) + + +@app.route("/normal_argbyname") +def normal_argbyname(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=unsafe_filter) + + +@app.route("/direct_argbyname") +def direct_argbyname(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + user = ldap.initialize("ldap://127.0.0.1:1337").search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=unsafe_filter) + + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py new file mode 100644 index 00000000000..1d04505fa28 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py @@ -0,0 +1,60 @@ +from flask import request, Flask +import ldap +import ldap.filter +import ldap.dn + +app = Flask(__name__) + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + safe_dn, ldap.SCOPE_SUBTREE, safe_filter, ["testAttr1", "testAttr2"]) + + +@app.route("/direct") +def direct(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + user = ldap.initialize("ldap://127.0.0.1:1337").search_s( + safe_dn, ldap.SCOPE_SUBTREE, safe_filter, ["testAttr1", "testAttr2"]) + + +@app.route("/normal_argbyname") +def normal_argbyname(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + safe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=safe_filter) + + +@app.route("/direct_argbyname") +def direct_argbyname(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + user = ldap.initialize("ldap://127.0.0.1:1337").search_s( + safe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=safe_filter) + + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 904b7967ee8..e2b278e40da 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,3 +13,46 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks +private import semmle.python.ApiGraphs + +/** + * To-Do + * + * LDAPQuery -> collect functions executing a search filter/DN + * LDAPEscape -> collect functions escaping a search filter/DN + */ +module LDAPQuery { + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getLDAPNode(); + + abstract string getLDAPPart(); + + abstract DataFlow::Node getAttrs(); + } +} + +class LDAPQuery extends DataFlow::Node { + LDAPQuery::Range range; + + LDAPQuery() { this = range } + + DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } + + string getLDAPPart() { result = range.getLDAPPart() } + + DataFlow::Node getAttrs() { result = range.getAttrs() } +} + +module LDAPEscape { + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getEscapeNode(); + } +} + +class LDAPEscape extends DataFlow::Node { + LDAPEscape::Range range; + + LDAPEscape() { this = range } + + DataFlow::Node getEscapeNode() { result = range.getEscapeNode() } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 420caf0d73b..1ef42edbdbb 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -9,3 +9,59 @@ private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs + +private module LDAP { + private module LDAP2 { + private class LDAP2QueryMethods extends string { + LDAP2QueryMethods() { + this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"] + } + } + + private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { + DataFlow::Node ldapNode; + string ldapPart; + DataFlow::Node attrs; + + LDAP2Query() { + exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode initCall | + this.getFunction() = searchMethod and + initCall = API::moduleImport("ldap").getMember("initialize").getACall() and + initCall = searchMethod.getObject().getALocalSource() and + searchMethod.getAttributeName() instanceof LDAP2QueryMethods and + ( + ( + ldapNode = this.getArg(2) or + ldapNode = this.getArgByName("filterstr") + ) and + ldapPart = "search_filter" + or + ldapNode = this.getArg(0) and + ldapPart = "DN" + ) and + ( // what if they're not set? + attrs = this.getArg(3) or + attrs = this.getArgByName("attrlist") + ) + ) + } + + override DataFlow::Node getLDAPNode() { result = ldapNode } + + override string getLDAPPart() { result = ldapPart } + + override DataFlow::Node getAttrs() { result = attrs } + } + + private class LDAP2EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { + DataFlow::Node escapeNode; + + LDAP2EscapeDN() { + this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() and + escapeNode = this.getArg(0) + } + + override DataFlow::Node getEscapeNode() { result = escapeNode } + } + } +} diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll new file mode 100644 index 00000000000..f5fa2306f31 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll @@ -0,0 +1,21 @@ +/** + * Provides a taint-tracking configuration for detecting LDAP injection vulnerabilities + */ + +import python +import experimental.semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for detecting regular expression injections. + */ +class LDAPInjectionFlowConfig extends TaintTracking::Configuration { + LDAPInjectionFlowConfig() { this = "LDAPInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery lQ).getLDAPNode() } + // override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RemoteFlowSource } // any(LDAPEscape ldapEsc).getEscapeNode() } +} From ad36bea9d4ce61f804cdbfedddc5d3bebecdf1f6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Mon, 29 Mar 2021 09:14:35 +0200 Subject: [PATCH 005/550] Refactor LDAP3 stuff (untested) --- .../Security/CWE-090/LDAPInjection.ql | 5 +- .../experimental/semmle/python/Concepts.qll | 6 -- .../semmle/python/frameworks/Stdlib.qll | 95 +++++++++++++++++-- .../security/injection/LDAPInjection.qll | 5 +- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 778b771f883..7ce3a118918 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -19,7 +19,8 @@ from LDAPQuery castedSink where config.hasFlowPath(source, sink) and - castedSink = sink.getNode() + castedSink = sink.getNode() //and +// if exists(castedSink.getAttrs()) then select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@ probably leaking $@.", sink.getNode(), "This", source.getNode(), "a user-provided value", castedSink.getLDAPNode(), - castedSink.getLDAPPart(), castedSink.getAttrList(), "this attribute(s)" + castedSink.getLDAPPart(), castedSink.getAttrs(), "this attribute(s)" diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index e2b278e40da..e0f5c1bdec3 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -15,12 +15,6 @@ private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks private import semmle.python.ApiGraphs -/** - * To-Do - * - * LDAPQuery -> collect functions executing a search filter/DN - * LDAPEscape -> collect functions escaping a search filter/DN - */ module LDAPQuery { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getLDAPNode(); diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 1ef42edbdbb..aa9013a76c0 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -30,18 +30,14 @@ private module LDAP { initCall = searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP2QueryMethods and ( + ldapNode = this.getArg(0) and + ldapPart = "DN" + or ( ldapNode = this.getArg(2) or ldapNode = this.getArgByName("filterstr") ) and ldapPart = "search_filter" - or - ldapNode = this.getArg(0) and - ldapPart = "DN" - ) and - ( // what if they're not set? - attrs = this.getArg(3) or - attrs = this.getArgByName("attrlist") ) ) } @@ -50,7 +46,9 @@ private module LDAP { override string getLDAPPart() { result = ldapPart } - override DataFlow::Node getAttrs() { result = attrs } + override DataFlow::Node getAttrs() { + result = this.getArg(3) or result = this.getArgByName("attrlist") + } } private class LDAP2EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { @@ -63,5 +61,86 @@ private module LDAP { override DataFlow::Node getEscapeNode() { result = escapeNode } } + + private class LDAP2EscapeFilter extends DataFlow::CallCfgNode, LDAPEscape::Range { + DataFlow::Node escapeNode; + + LDAP2EscapeFilter() { + this = + API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() and + escapeNode = this.getArg(0) + } + + override DataFlow::Node getEscapeNode() { result = escapeNode } + } + } + + private module LDAP3 { + private class LDAP3QueryMethods extends string { + // pending to dig into this although https://github.com/cannatag/ldap3/blob/21001d9087c0d24c399eec433a261c455b7bc97f/ldap3/core/connection.py#L760 + LDAP3QueryMethods() { this in ["search"] } + } + + private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { + DataFlow::Node ldapNode; + string ldapPart; + DataFlow::Node attrs; + + LDAP3Query() { + exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode connCall | + this.getFunction() = searchMethod and + connCall = API::moduleImport("ldap3").getMember("Connection").getACall() and + connCall = searchMethod.getObject().getALocalSource() and + searchMethod.getAttributeName() instanceof LDAP3QueryMethods and + ( + ldapNode = this.getArg(0) and + ldapPart = "DN" + or + ldapNode = this.getArg(1) and + ldapPart = "search_filter" + ) + ) + } + + override DataFlow::Node getLDAPNode() { result = ldapNode } + + override string getLDAPPart() { result = ldapPart } + + override DataFlow::Node getAttrs() { + result = this.getArg(3) or result = this.getArgByName("attributes") + } + } + + private class LDAP3EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { + DataFlow::Node escapeNode; + + LDAP3EscapeDN() { + this = + API::moduleImport("ldap3") + .getMember("utils") + .getMember("dn") + .getMember("escape_rdn") + .getACall() and + escapeNode = this.getArg(0) + } + + override DataFlow::Node getEscapeNode() { result = escapeNode } + } + + private class LDAP3EscapeFilter extends DataFlow::CallCfgNode, LDAPEscape::Range { + DataFlow::Node escapeNode; + + LDAP3EscapeFilter() { + this = + API::moduleImport("ldap3") + .getMember("utils") + .getMember("conv") + .getMember("escape_filter_chars") + .getACall() and + escapeNode = this.getArg(0) + } + + override DataFlow::Node getEscapeNode() { result = escapeNode } + } } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll index f5fa2306f31..db8bdda7db6 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll @@ -17,5 +17,8 @@ class LDAPInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery lQ).getLDAPNode() } - // override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RemoteFlowSource } // any(LDAPEscape ldapEsc).getEscapeNode() } + + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = any(LDAPEscape lE).getEscapeNode() + } } From 8223539f0c0988e4d1ef79710c80c82533b12901 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Mon, 29 Mar 2021 23:28:28 +0200 Subject: [PATCH 006/550] Add a test without attributes --- .../Security/CWE-090/unit_tests/ldap_bad.py | 10 ++++++++++ .../Security/CWE-090/unit_tests/ldap_good.py | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py index a409a38e019..011f3b82865 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py @@ -14,6 +14,16 @@ def normal(): unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter, ["testAttr1", "testAttr2"]) +@app.route("/normal_noAttrs") +def normal_noAttrs(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter) + + @app.route("/direct") def direct(): unsafe_dn = "dc=%s" % request.args['dc'] diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py index 1d04505fa28..eda3883da6d 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py @@ -19,6 +19,19 @@ def normal(): safe_dn, ldap.SCOPE_SUBTREE, safe_filter, ["testAttr1", "testAttr2"]) +@app.route("/normal_noAttrs") +def normal_noAttrs(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + safe_dn, ldap.SCOPE_SUBTREE, safe_filter) + + @app.route("/direct") def direct(): unsafe_dn = "dc=%s" % request.args['dc'] From 3cda2e52073b72dfc126f7863041a85005d818ae Mon Sep 17 00:00:00 2001 From: jorgectf Date: Mon, 29 Mar 2021 23:39:49 +0200 Subject: [PATCH 007/550] Polish up ldap3 tests --- .../Security/CWE-090/ldap3_bad.py | 56 ------------------ .../Security/CWE-090/ldap3_good.py | 58 ------------------- .../Security/CWE-090/unit_tests/ldap3_bad.py | 38 ++++++++++++ .../Security/CWE-090/unit_tests/ldap3_good.py | 49 ++++++++++++++++ 4 files changed, 87 insertions(+), 114 deletions(-) delete mode 100644 python/ql/src/experimental/Security/CWE-090/ldap3_bad.py delete mode 100644 python/ql/src/experimental/Security/CWE-090/ldap3_good.py create mode 100644 python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py create mode 100644 python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py diff --git a/python/ql/src/experimental/Security/CWE-090/ldap3_bad.py b/python/ql/src/experimental/Security/CWE-090/ldap3_bad.py deleted file mode 100644 index fcd00c0269c..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/ldap3_bad.py +++ /dev/null @@ -1,56 +0,0 @@ -import ldap3 -from flask import request, Flask - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - unsanitized_dn = "dc=%s" % request.args['dc'] - unsanitized_filter = "(&(objectClass=*)(uid=%s))" % request.args['username'] - - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user=unsanitized_dn, auto_bind=True) - conn.search(unsanitized_dn, unsanitized_filter) - return conn.response - - -@app.route("/var_tainted") -def var_tainted(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user=dn, auto_bind=True) - conn.search(dn, search_filter) - return conn.response - - -@app.route("/direct") -def direct(): - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user="dc=%s" % - request.args['dc'], auto_bind=True) - conn.search("dc=%s" % unsanitized_dn, - "(&(objectClass=*)(uid=%s))" % request.args['username']) - return conn.response - - -@app.route("/with_") -def with_(): - unsanitized_dn = request.args['dc'] - unsanitized_filter = request.args['username'] - - dn = "dc=%s" % unsanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % unsanitized_filter - - srv = ldap3.Server('localhost', port=1337) - with ldap3.Connection(server, auto_bind=True) as conn: - conn.search(dn, search_filter) - return conn.response - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/ldap3_good.py b/python/ql/src/experimental/Security/CWE-090/ldap3_good.py deleted file mode 100644 index 05bc1e24f71..00000000000 --- a/python/ql/src/experimental/Security/CWE-090/ldap3_good.py +++ /dev/null @@ -1,58 +0,0 @@ -import ldap3 -from ldap3.utils.conv import escape_filter_chars -from flask import request, Flask - -app = Flask(__name__) - - -@app.route("/tainted_var") -def tainted_var(): - sanitized_dn = "dc=%s" % request.args['dc'] - sanitized_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars( - request.args['username']) - - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user=sanitized_dn, auto_bind=True) - conn.search(sanitized_dn, sanitized_filter) - return conn.response - - -@app.route("/var_tainted") -def var_tainted(): - sanitized_dn = request.args['dc'] - sanitized_filter = request.args['username'] - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % escape_filter_chars(sanitized_filter) - - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user=dn, auto_bind=True) - conn.search(dn, search_filter) - return conn.response - - -@app.route("/direct") -def direct(): - srv = ldap3.Server('localhost', port=1337) - conn = ldap3.Connection(srv, user="dc=%s" % - request.args['dc'], auto_bind=True) - conn.search("dc=%s" % request.args['dc'], "(&(objectClass=*)(uid=%s))" % - escape_filter_chars(request.args['username'])) - return conn.response - - -@ app.route("/with_") -def with_(): - sanitized_dn = request.args['dc'] - sanitized_filter = escape_filter_chars(request.args['username']) - - dn = "dc=%s" % sanitized_dn - search_filter = "(&(objectClass=*)(uid=%s))" % sanitized_filter - - srv = ldap3.Server('localhost', port=1337) - with ldap3.Connection(server, auto_bind=True) as conn: - conn.search(dn, search_filter) - return conn.response - -# if __name__ == "__main__": -# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py new file mode 100644 index 00000000000..76b7f309bd1 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py @@ -0,0 +1,38 @@ +from flask import request, Flask +import ldap3 + +app = Flask(__name__) + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn.search(unsafe_dn, unsafe_filter, attributes=[ + "testAttr1", "testAttr2"]) + + +@app.route("/normal_noAttrs") +def normal_noAttrs(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn.search(unsafe_dn, unsafe_filter) + + +@app.route("/direct") +def direct(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True).search(unsafe_dn, unsafe_filter, attributes=[ + "testAttr1", "testAttr2"]) + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py new file mode 100644 index 00000000000..c3cc1272ea7 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py @@ -0,0 +1,49 @@ +from flask import request, Flask +import ldap3 +from ldap3.utils.dn import escape_rdn +from ldap3.utils.conv import escape_filter_chars + +app = Flask(__name__) + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = escape_rdn(unsafe_dn) + safe_filter = escape_filter_chars(unsafe_filter) + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn.search(safe_dn, safe_filter, attributes=[ + "testAttr1", "testAttr2"]) + + +@app.route("/normal_noAttrs") +def normal_noAttrs(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = escape_rdn(unsafe_dn) + safe_filter = escape_filter_chars(unsafe_filter) + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn.search(safe_dn, safe_filter) + + +@app.route("/direct") +def direct(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = escape_rdn(unsafe_dn) + safe_filter = escape_filter_chars(unsafe_filter) + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True).search(safe_dn, safe_filter, attributes=[ + "testAttr1", "testAttr2"]) + +# if __name__ == "__main__": +# app.run(debug=True) From 8faafb6961d21e652ff3c7223dab498cec9f8332 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 30 Mar 2021 16:58:02 +0200 Subject: [PATCH 008/550] Update Sink --- .../Security/CWE-090/LDAPInjection.ql | 9 +++---- .../semmle/python/frameworks/Stdlib.qll | 6 ++--- .../security/injection/LDAPInjection.qll | 26 +++++++++++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 7ce3a118918..a803b39260e 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -16,11 +16,10 @@ import DataFlow::PathGraph from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - LDAPQuery castedSink + LDAPInjectionSink castedSink where config.hasFlowPath(source, sink) and - castedSink = sink.getNode() //and + castedSink.getLDAPNode() = sink.getNode() //and // if exists(castedSink.getAttrs()) then -select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@ probably leaking $@.", - sink.getNode(), "This", source.getNode(), "a user-provided value", castedSink.getLDAPNode(), - castedSink.getLDAPPart(), castedSink.getAttrs(), "this attribute(s)" +select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@.", castedSink, "This", + source.getNode(), "a user-provided value", castedSink.getLDAPNode(), castedSink.getLDAPPart() //, castedSink.getAttrs(), "probably leaking this attribute(s)" diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index aa9013a76c0..348367d0370 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -95,10 +95,10 @@ private module LDAP { ( ldapNode = this.getArg(0) and ldapPart = "DN" - or - ldapNode = this.getArg(1) and - ldapPart = "search_filter" ) + or + ldapNode = this.getArg(1) and + ldapPart = "search_filter" ) } diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll index db8bdda7db6..4814097ff8e 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll @@ -8,6 +8,26 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +class LDAPInjectionSink extends DataFlow::Node { + // DataFlow::Node attrs; + DataFlow::Node ldapNode; + string ldapPart; + + LDAPInjectionSink() { + exists(LDAPQuery ldapQuery | + this = ldapQuery and + ldapNode = ldapQuery.getLDAPNode() and + ldapPart = ldapQuery.getLDAPPart() // and + // if exists(ldapQuery.getAttrs()) then attrs = ldapQuery.getAttrs() + ) + } + + DataFlow::Node getLDAPNode() { result = ldapNode } + + string getLDAPPart() { result = ldapPart } + // DataFlow::Node getAttrs() { result = attrs } +} + /** * A taint-tracking configuration for detecting regular expression injections. */ @@ -16,9 +36,11 @@ class LDAPInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery lQ).getLDAPNode() } + override predicate isSink(DataFlow::Node sink) { + sink = any(LDAPInjectionSink ldapInjSink).getLDAPNode() + } override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = any(LDAPEscape lE).getEscapeNode() + sanitizer = any(LDAPEscape ldapEsc).getEscapeNode() } } From 4328ff398121d12f800a67a3edc0cd72ea2bf77e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 31 Mar 2021 22:26:08 +0200 Subject: [PATCH 009/550] Remove attrs feature --- .../experimental/Security/CWE-090/LDAPInjection.ql | 5 ++--- python/ql/src/experimental/semmle/python/Concepts.qll | 4 ---- .../experimental/semmle/python/frameworks/Stdlib.qll | 11 ----------- .../python/security/injection/LDAPInjection.qll | 5 +---- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index a803b39260e..8d01fc173d4 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -19,7 +19,6 @@ from LDAPInjectionSink castedSink where config.hasFlowPath(source, sink) and - castedSink.getLDAPNode() = sink.getNode() //and -// if exists(castedSink.getAttrs()) then + castedSink.getLDAPNode() = sink.getNode() select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@.", castedSink, "This", - source.getNode(), "a user-provided value", castedSink.getLDAPNode(), castedSink.getLDAPPart() //, castedSink.getAttrs(), "probably leaking this attribute(s)" + source.getNode(), "a user-provided value", castedSink.getLDAPNode(), castedSink.getLDAPPart() diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index e0f5c1bdec3..e62d9680665 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -20,8 +20,6 @@ module LDAPQuery { abstract DataFlow::Node getLDAPNode(); abstract string getLDAPPart(); - - abstract DataFlow::Node getAttrs(); } } @@ -33,8 +31,6 @@ class LDAPQuery extends DataFlow::Node { DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } string getLDAPPart() { result = range.getLDAPPart() } - - DataFlow::Node getAttrs() { result = range.getAttrs() } } module LDAPEscape { diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 348367d0370..f56a6603ec7 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -21,7 +21,6 @@ private module LDAP { private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; string ldapPart; - DataFlow::Node attrs; LDAP2Query() { exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode initCall | @@ -45,10 +44,6 @@ private module LDAP { override DataFlow::Node getLDAPNode() { result = ldapNode } override string getLDAPPart() { result = ldapPart } - - override DataFlow::Node getAttrs() { - result = this.getArg(3) or result = this.getArgByName("attrlist") - } } private class LDAP2EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { @@ -77,14 +72,12 @@ private module LDAP { private module LDAP3 { private class LDAP3QueryMethods extends string { - // pending to dig into this although https://github.com/cannatag/ldap3/blob/21001d9087c0d24c399eec433a261c455b7bc97f/ldap3/core/connection.py#L760 LDAP3QueryMethods() { this in ["search"] } } private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; string ldapPart; - DataFlow::Node attrs; LDAP3Query() { exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode connCall | @@ -105,10 +98,6 @@ private module LDAP { override DataFlow::Node getLDAPNode() { result = ldapNode } override string getLDAPPart() { result = ldapPart } - - override DataFlow::Node getAttrs() { - result = this.getArg(3) or result = this.getArgByName("attributes") - } } private class LDAP3EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll index 4814097ff8e..da71f38457a 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll @@ -9,7 +9,6 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources class LDAPInjectionSink extends DataFlow::Node { - // DataFlow::Node attrs; DataFlow::Node ldapNode; string ldapPart; @@ -17,15 +16,13 @@ class LDAPInjectionSink extends DataFlow::Node { exists(LDAPQuery ldapQuery | this = ldapQuery and ldapNode = ldapQuery.getLDAPNode() and - ldapPart = ldapQuery.getLDAPPart() // and - // if exists(ldapQuery.getAttrs()) then attrs = ldapQuery.getAttrs() + ldapPart = ldapQuery.getLDAPPart() ) } DataFlow::Node getLDAPNode() { result = ldapNode } string getLDAPPart() { result = ldapPart } - // DataFlow::Node getAttrs() { result = attrs } } /** From 9b430310b4ecd9535191262249d889d0b9306025 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 31 Mar 2021 23:19:56 +0200 Subject: [PATCH 010/550] Improve Sanitizer calls --- .../semmle/python/frameworks/Stdlib.qll | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index f56a6603ec7..cfd02b8b5a5 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -46,27 +46,21 @@ private module LDAP { override string getLDAPPart() { result = ldapPart } } - private class LDAP2EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { - DataFlow::Node escapeNode; - - LDAP2EscapeDN() { - this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() and - escapeNode = this.getArg(0) + private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP2EscapeDNCall() { + this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() } - override DataFlow::Node getEscapeNode() { result = escapeNode } + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } - private class LDAP2EscapeFilter extends DataFlow::CallCfgNode, LDAPEscape::Range { - DataFlow::Node escapeNode; - - LDAP2EscapeFilter() { + private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP2EscapeFilterCall() { this = - API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() and - escapeNode = this.getArg(0) + API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() } - override DataFlow::Node getEscapeNode() { result = escapeNode } + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } } @@ -100,36 +94,30 @@ private module LDAP { override string getLDAPPart() { result = ldapPart } } - private class LDAP3EscapeDN extends DataFlow::CallCfgNode, LDAPEscape::Range { - DataFlow::Node escapeNode; - - LDAP3EscapeDN() { + private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP3EscapeDNCall() { this = API::moduleImport("ldap3") .getMember("utils") .getMember("dn") .getMember("escape_rdn") - .getACall() and - escapeNode = this.getArg(0) + .getACall() } - override DataFlow::Node getEscapeNode() { result = escapeNode } + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } - private class LDAP3EscapeFilter extends DataFlow::CallCfgNode, LDAPEscape::Range { - DataFlow::Node escapeNode; - - LDAP3EscapeFilter() { + private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP3EscapeFilterCall() { this = API::moduleImport("ldap3") .getMember("utils") .getMember("conv") .getMember("escape_filter_chars") - .getACall() and - escapeNode = this.getArg(0) + .getACall() } - override DataFlow::Node getEscapeNode() { result = escapeNode } + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } } } From 1bcb9cd7c0f9be99c8c30e7bf4d4fea93722efb7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 6 Apr 2021 15:42:56 +0200 Subject: [PATCH 011/550] Simplify query --- .../Security/CWE-090/LDAPInjection.ql | 12 ++++------- .../experimental/semmle/python/Concepts.qll | 5 ----- .../semmle/python/frameworks/Stdlib.qll | 19 ++++------------- .../security/injection/LDAPInjection.qll | 21 +------------------ 4 files changed, 9 insertions(+), 48 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 8d01fc173d4..945a7b94473 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -14,11 +14,7 @@ import python import experimental.semmle.python.security.injection.LDAPInjection import DataFlow::PathGraph -from - LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - LDAPInjectionSink castedSink -where - config.hasFlowPath(source, sink) and - castedSink.getLDAPNode() = sink.getNode() -select sink.getNode(), source, sink, "$@ LDAP query executes $@ as a $@.", castedSink, "This", - source.getNode(), "a user-provided value", castedSink.getLDAPNode(), castedSink.getLDAPPart() +from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ LDAP query parameter comes from $@.", sink.getNode(), + "This", source.getNode(), "a user-provided value" diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index e62d9680665..9022f9c3818 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,13 +13,10 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -private import semmle.python.ApiGraphs module LDAPQuery { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getLDAPNode(); - - abstract string getLDAPPart(); } } @@ -29,8 +26,6 @@ class LDAPQuery extends DataFlow::Node { LDAPQuery() { this = range } DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } - - string getLDAPPart() { result = range.getLDAPPart() } } module LDAPEscape { diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index cfd02b8b5a5..5a7984fab43 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -20,7 +20,6 @@ private module LDAP { private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; - string ldapPart; LDAP2Query() { exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode initCall | @@ -29,21 +28,17 @@ private module LDAP { initCall = searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP2QueryMethods and ( - ldapNode = this.getArg(0) and - ldapPart = "DN" + ldapNode = this.getArg(0) or ( ldapNode = this.getArg(2) or ldapNode = this.getArgByName("filterstr") - ) and - ldapPart = "search_filter" + ) ) ) } override DataFlow::Node getLDAPNode() { result = ldapNode } - - override string getLDAPPart() { result = ldapPart } } private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { @@ -71,7 +66,6 @@ private module LDAP { private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; - string ldapPart; LDAP3Query() { exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode connCall | @@ -80,18 +74,13 @@ private module LDAP { connCall = searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP3QueryMethods and ( - ldapNode = this.getArg(0) and - ldapPart = "DN" + ldapNode = this.getArg(0) or + ldapNode = this.getArg(1) ) - or - ldapNode = this.getArg(1) and - ldapPart = "search_filter" ) } override DataFlow::Node getLDAPNode() { result = ldapNode } - - override string getLDAPPart() { result = ldapPart } } private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll index da71f38457a..febebe0a8fd 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll @@ -8,23 +8,6 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources -class LDAPInjectionSink extends DataFlow::Node { - DataFlow::Node ldapNode; - string ldapPart; - - LDAPInjectionSink() { - exists(LDAPQuery ldapQuery | - this = ldapQuery and - ldapNode = ldapQuery.getLDAPNode() and - ldapPart = ldapQuery.getLDAPPart() - ) - } - - DataFlow::Node getLDAPNode() { result = ldapNode } - - string getLDAPPart() { result = ldapPart } -} - /** * A taint-tracking configuration for detecting regular expression injections. */ @@ -33,9 +16,7 @@ class LDAPInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { - sink = any(LDAPInjectionSink ldapInjSink).getLDAPNode() - } + override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery ldapQuery).getLDAPNode() } override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer = any(LDAPEscape ldapEsc).getEscapeNode() From 59ff3f315b34bdb4989ea17d9c8f9387bdc821d5 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 31 Mar 2021 19:11:28 +0100 Subject: [PATCH 012/550] C++: Add test cases exploring issues and potential issues with the query (especially related to simple range analysis). --- ...dDifferenceExpressionComparedZero.expected | 18 ++ .../test.cpp | 192 +++++++++++++++++- 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 2d28795d126..447d98c1fd7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -8,3 +8,21 @@ | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:83:6:83:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:92:6:92:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:110:6:110:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:119:6:119:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:156:7:156:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:169:6:169:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:195:6:195:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:219:7:219:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:226:8:226:16 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index 11de69e8c62..c16608ef0f8 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -74,4 +74,194 @@ void test(unsigned x, unsigned y, bool unknown) { y += n; // NOTE: `n` is at most `x - y` at this point. if (x - y > 0) {} // GOOD [FALSE POSITIVE] } -} \ No newline at end of file +} + +void test2() { + unsigned int a = getAnInt(); + unsigned int b = a; + + if (a - b > 0) { // GOOD (as a = b) [FALSE POSITIVE] + // ... + } +} + +void test3() { + unsigned int a = getAnInt(); + unsigned int b = a - 1; + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test4() { + unsigned int a = getAnInt(); + unsigned int b = a + 1; + + if (a - b > 0) { // BAD + // ... + } +} + +void test5() { + unsigned int b = getAnInt(); + unsigned int a = b; + + if (a - b > 0) { // GOOD (as a = b) [FALSE POSITIVE] + // ... + } +} + +void test6() { + unsigned int b = getAnInt(); + unsigned int a = b + 1; + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test7() { + unsigned int b = getAnInt(); + unsigned int a = b - 1; + + if (a - b > 0) { // BAD + // ... + } +} + +void test8() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a - b > 0) { // BAD + // ... + } + + if (a >= b) { // GOOD + if (a - b > 0) { // GOOD (as a >= b) + // ... + } + } else { + if (a - b > 0) { // BAD + // ... + } + } + + if (b >= a) { // GOOD + if (a - b > 0) { // BAD + // ... + } + } else { + if (a - b > 0) { // GOOD (as a > b) [FALSE POSITIVE] + // ... + } + } + + while (a >= b) { // GOOD + if (a - b > 0) { // GOOD (as a >= b) + // ... + } + } + + if (a < b) return; + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test9() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) { + b = 0; + } + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test10() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) { + a = b; + } + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test11() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) return; + + b = getAnInt(); + + if (a - b > 0) { // BAD + // ... + } +} + +void test12() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + unsigned int c; + + if ((b <= c) && (c <= a)) { + if (a - b > 0) { // GOOD (as b <= a) [FALSE POSITIVE] + // ... + } + } + + if (b <= c) { + if (c <= a) { + if (a - b > 0) { // GOOD (as b <= a) [FALSE POSITIVE] + // ... + } + } + } +} + +int test13() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b != 0) { + return 0; + } + + return (a - b > 0); // GOOD (as b = 0) +} + +int test14() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (!b) { + return 0; + } + + return (a - b > 0); // GOOD (as b = 0) [FALSE POSITIVE] +} + +struct Numbers +{ + unsigned int a, b; +}; + +int test15(Numbers *n) { + + if (!n) { + return 0; + } + + return (n->a - n->b > 0); // BAD +} From 3ecd13531f7be2d4ee5c7bd5733bde6be1217439 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 Apr 2021 17:59:22 +0100 Subject: [PATCH 013/550] C++: Improve isGuarded. --- .../UnsignedDifferenceExpressionComparedZero.ql | 12 ++++++++---- ...UnsignedDifferenceExpressionComparedZero.expected | 2 -- .../test.cpp | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 007a4fd746d..0b30cc7e213 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -16,12 +16,16 @@ import semmle.code.cpp.valuenumbering.GlobalValueNumbering import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.controlflow.Guards -/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */ +/** + * Holds if `sub` is guarded by a condition which ensures that + * `left >= right`. + */ pragma[noinline] predicate isGuarded(SubExpr sub, Expr left, Expr right) { - exists(GuardCondition guard | - guard.controls(sub.getBasicBlock(), true) and - guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false) + exists(GuardCondition guard, int k | + guard.controls(sub.getBasicBlock(), _) and + guard.ensuresLt(left, right, k, sub.getBasicBlock(), false) and + k >= 0 ) } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 447d98c1fd7..36dc7cf4c5b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -17,8 +17,6 @@ | test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:156:7:156:15 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:169:6:169:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:195:6:195:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index c16608ef0f8..bf2b2eb5dad 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -153,7 +153,7 @@ void test8() { // ... } } else { - if (a - b > 0) { // GOOD (as a > b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a > b) // ... } } @@ -166,7 +166,7 @@ void test8() { if (a < b) return; - if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a >= b) // ... } } From 48ff8e237c1aba54368196193c2fa09b109e771e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 Apr 2021 15:57:58 +0100 Subject: [PATCH 014/550] C++: Rewrite the range analysis exclusion to be recursive and more robust. --- ...nsignedDifferenceExpressionComparedZero.ql | 26 ++++++++++++------- ...dDifferenceExpressionComparedZero.expected | 5 +--- .../test.cpp | 10 +++---- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 0b30cc7e213..26c2c6ae869 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -29,16 +29,22 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { ) } -/** Holds if `sub` will never be negative. */ -predicate nonNegative(SubExpr sub) { - not exprMightOverflowNegatively(sub.getFullyConverted()) +/** + * Holds if `e` is known to be less than or equal to `sub.getLeftOperand()`. + */ +predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { + e = sub.getLeftOperand() or - // The subtraction is guarded by a check of the form `left >= right`. - exists(GVN left, GVN right | - // This is basically a poor man's version of a directional unbind operator. - strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and - strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and - isGuarded(sub, left.getAnExpr(), right.getAnExpr()) + exists(Expr other | + // GVN equality + exprIsSubLeftOrLess(sub, other) and + globalValueNumber(e) = globalValueNumber(other) + ) + or + exists(Expr other | + // guard constraining `sub` + exprIsSubLeftOrLess(sub, other) and + isGuarded(sub, other, e) // left >= right ) } @@ -49,5 +55,5 @@ where ro.getLesserOperand().getValue().toInt() = 0 and ro.getGreaterOperand() = sub and sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and - not nonNegative(sub) + not exprIsSubLeftOrLess(sub, sub.getRightOperand()) select ro, "Unsigned subtraction can never be negative." diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 36dc7cf4c5b..0acb75de56f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -8,10 +8,8 @@ | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:83:6:83:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:92:6:92:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:110:6:110:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:119:6:119:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. | @@ -20,7 +18,6 @@ | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:195:6:195:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:219:7:219:15 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:226:8:226:16 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:241:10:241:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index bf2b2eb5dad..e42ffd8d326 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -80,7 +80,7 @@ void test2() { unsigned int a = getAnInt(); unsigned int b = a; - if (a - b > 0) { // GOOD (as a = b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a = b) // ... } } @@ -107,7 +107,7 @@ void test5() { unsigned int b = getAnInt(); unsigned int a = b; - if (a - b > 0) { // GOOD (as a = b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a = b) // ... } } @@ -216,14 +216,14 @@ void test12() { unsigned int c; if ((b <= c) && (c <= a)) { - if (a - b > 0) { // GOOD (as b <= a) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as b <= a) // ... } } if (b <= c) { if (c <= a) { - if (a - b > 0) { // GOOD (as b <= a) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as b <= a) // ... } } @@ -238,7 +238,7 @@ int test13() { return 0; } - return (a - b > 0); // GOOD (as b = 0) + return (a - b > 0); // GOOD (as b = 0) [FALSE POSITIVE] } int test14() { From 60e4faba4c24686ad74ba45197ccd38c289b91f5 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 Apr 2021 18:30:18 +0100 Subject: [PATCH 015/550] C++: Add linear expression logic. --- .../UnsignedDifferenceExpressionComparedZero.ql | 17 +++++++++++++++++ ...nedDifferenceExpressionComparedZero.expected | 2 -- .../test.cpp | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 26c2c6ae869..8073a3567d2 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -14,6 +14,7 @@ import cpp import semmle.code.cpp.commons.Exclusions import semmle.code.cpp.valuenumbering.GlobalValueNumbering import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.controlflow.Guards /** @@ -46,6 +47,22 @@ predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { exprIsSubLeftOrLess(sub, other) and isGuarded(sub, other, e) // left >= right ) + or + exists(Expr other, float p, float q | + // linear access of `other` + exprIsSubLeftOrLess(sub, other) and + linearAccess(e, other, p, q) and // e = p * other + q + p <= 1 and + q <= 0 + ) + or + exists(Expr other, float p, float q | + // linear access of `e` + exprIsSubLeftOrLess(sub, other) and + linearAccess(other, e, p, q) and // other = p * e + q + p >= 1 and + q >= 0 + ) } from RelationalOperation ro, SubExpr sub diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 0acb75de56f..06fcf0ebb72 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -8,9 +8,7 @@ | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:92:6:92:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:119:6:119:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index e42ffd8d326..c0a4d38d14a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -89,7 +89,7 @@ void test3() { unsigned int a = getAnInt(); unsigned int b = a - 1; - if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a >= b) // ... } } @@ -116,7 +116,7 @@ void test6() { unsigned int b = getAnInt(); unsigned int a = b + 1; - if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a >= b) // ... } } From a8193dac087f12d487eef9091dfe57135fe7690f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 6 Apr 2021 22:35:04 +0100 Subject: [PATCH 016/550] C++: Reintroduce the exprMightOverflowNegatively bit. --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 3 ++- .../UnsignedDifferenceExpressionComparedZero.expected | 1 - .../CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 8073a3567d2..bca77f99a72 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -72,5 +72,6 @@ where ro.getLesserOperand().getValue().toInt() = 0 and ro.getGreaterOperand() = sub and sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and - not exprIsSubLeftOrLess(sub, sub.getRightOperand()) + exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants + not exprIsSubLeftOrLess(sub, sub.getRightOperand()) // generally catches false positives where there's a relation between the left and right operands select ro, "Unsigned subtraction can never be negative." diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 06fcf0ebb72..46bf57ee52a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -16,6 +16,5 @@ | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:195:6:195:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:241:10:241:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index c0a4d38d14a..dfaedbb9d2b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -238,7 +238,7 @@ int test13() { return 0; } - return (a - b > 0); // GOOD (as b = 0) [FALSE POSITIVE] + return (a - b > 0); // GOOD (as b = 0) } int test14() { From 33423eaef3386b3e6e782c35fb2fb99c8a4cf150 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 00:31:53 +0200 Subject: [PATCH 017/550] Optimize calls --- .../experimental/semmle/python/frameworks/Stdlib.qll | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5a7984fab43..24def6be277 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -22,10 +22,10 @@ private module LDAP { DataFlow::Node ldapNode; LDAP2Query() { - exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode initCall | + exists(DataFlow::AttrRead searchMethod | this.getFunction() = searchMethod and - initCall = API::moduleImport("ldap").getMember("initialize").getACall() and - initCall = searchMethod.getObject().getALocalSource() and + API::moduleImport("ldap").getMember("initialize").getACall() = + searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP2QueryMethods and ( ldapNode = this.getArg(0) @@ -68,10 +68,10 @@ private module LDAP { DataFlow::Node ldapNode; LDAP3Query() { - exists(DataFlow::AttrRead searchMethod, DataFlow::CallCfgNode connCall | + exists(DataFlow::AttrRead searchMethod | this.getFunction() = searchMethod and - connCall = API::moduleImport("ldap3").getMember("Connection").getACall() and - connCall = searchMethod.getObject().getALocalSource() and + API::moduleImport("ldap3").getMember("Connection").getACall() = + searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP3QueryMethods and ( ldapNode = this.getArg(0) or From 517fd23ca59aa75899be0efb1a582436791ca8d8 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 8 Apr 2021 09:42:53 +0100 Subject: [PATCH 018/550] C++: Correct and add to test cases. --- ...dDifferenceExpressionComparedZero.expected | 2 + .../test.cpp | 39 +++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 46bf57ee52a..64a1aa0ab89 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -18,3 +18,5 @@ | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:276:11:276:19 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:288:10:288:18 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index dfaedbb9d2b..f151c593a2d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -236,7 +236,7 @@ int test13() { if (b != 0) { return 0; - } + } // b = 0 return (a - b > 0); // GOOD (as b = 0) } @@ -247,9 +247,9 @@ int test14() { if (!b) { return 0; - } + } // b != 0 - return (a - b > 0); // GOOD (as b = 0) [FALSE POSITIVE] + return (a - b > 0); // BAD } struct Numbers @@ -265,3 +265,36 @@ int test15(Numbers *n) { return (n->a - n->b > 0); // BAD } + +int test16() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (!b) { + return 0; + } else { + return (a - b > 0); // BAD + } +} + +int test17() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b == 0) { + return 0; + } // b != 0 + + return (a - b > 0); // BAD +} + +int test18() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b) { + return 0; + } // b == 0 + + return (a - b > 0); // GOOD (as b = 0) +} From a1850ddad47a07e93e9c2f94b10db2827fc5d863 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:55:48 +0200 Subject: [PATCH 019/550] Change LDAP config (qll) filename --- python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql | 2 +- .../python/security/injection/{LDAPInjection.qll => LDAP.qll} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename python/ql/src/experimental/semmle/python/security/injection/{LDAPInjection.qll => LDAP.qll} (100%) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 945a7b94473..28748595c46 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -11,7 +11,7 @@ // Determine precision above import python -import experimental.semmle.python.security.injection.LDAPInjection +import experimental.semmle.python.security.injection.LDAP import DataFlow::PathGraph from LDAPInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll similarity index 100% rename from python/ql/src/experimental/semmle/python/security/injection/LDAPInjection.qll rename to python/ql/src/experimental/semmle/python/security/injection/LDAP.qll From 8661cb071948d0311660ff720842f4e42d85d399 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:56:12 +0200 Subject: [PATCH 020/550] Polish LDAP3Query --- .../ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 24def6be277..76d4f326878 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -60,10 +60,6 @@ private module LDAP { } private module LDAP3 { - private class LDAP3QueryMethods extends string { - LDAP3QueryMethods() { this in ["search"] } - } - private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; @@ -72,7 +68,7 @@ private module LDAP { this.getFunction() = searchMethod and API::moduleImport("ldap3").getMember("Connection").getACall() = searchMethod.getObject().getALocalSource() and - searchMethod.getAttributeName() instanceof LDAP3QueryMethods and + searchMethod.getAttributeName() = "search" and ( ldapNode = this.getArg(0) or ldapNode = this.getArg(1) From 7296879bc9f1b40a508d69e74c6f047c6395e009 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:11:20 +0200 Subject: [PATCH 021/550] Polish tests --- .../Security/CWE-090/unit_tests/ldap3_bad.py | 21 +++++----- .../Security/CWE-090/unit_tests/ldap3_good.py | 28 ++++++-------- .../Security/CWE-090/unit_tests/ldap_bad.py | 34 +++++++---------- .../Security/CWE-090/unit_tests/ldap_good.py | 38 +++++++------------ 4 files changed, 47 insertions(+), 74 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py index 76b7f309bd1..a5b4748c146 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py @@ -6,17 +6,10 @@ app = Flask(__name__) @app.route("/normal") def normal(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] + """ + A RemoteFlowSource is used directly as DN and search filter + """ - srv = ldap3.Server('ldap://127.0.0.1', port=1337) - conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) - conn.search(unsafe_dn, unsafe_filter, attributes=[ - "testAttr1", "testAttr2"]) - - -@app.route("/normal_noAttrs") -def normal_noAttrs(): unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -27,12 +20,16 @@ def normal_noAttrs(): @app.route("/direct") def direct(): + """ + A RemoteFlowSource is used directly as DN and search filter using a oneline call to .search + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] srv = ldap3.Server('ldap://127.0.0.1', port=1337) - conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True).search(unsafe_dn, unsafe_filter, attributes=[ - "testAttr1", "testAttr2"]) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True).search( + unsafe_dn, unsafe_filter) # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py index c3cc1272ea7..a4fbb718c5b 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py @@ -8,6 +8,10 @@ app = Flask(__name__) @app.route("/normal") def normal(): + """ + A RemoteFlowSource is sanitized and used as DN and search filter + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -15,26 +19,16 @@ def normal(): safe_filter = escape_filter_chars(unsafe_filter) srv = ldap3.Server('ldap://127.0.0.1', port=1337) - conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) - conn.search(safe_dn, safe_filter, attributes=[ - "testAttr1", "testAttr2"]) - - -@app.route("/normal_noAttrs") -def normal_noAttrs(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] - - safe_dn = escape_rdn(unsafe_dn) - safe_filter = escape_filter_chars(unsafe_filter) - - srv = ldap3.Server('ldap://127.0.0.1', port=1337) - conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn = ldap3.Connection(srv, user=safe_dn, auto_bind=True) conn.search(safe_dn, safe_filter) @app.route("/direct") def direct(): + """ + A RemoteFlowSource is sanitized and used as DN and search filter using a oneline call to .search + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -42,8 +36,8 @@ def direct(): safe_filter = escape_filter_chars(unsafe_filter) srv = ldap3.Server('ldap://127.0.0.1', port=1337) - conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True).search(safe_dn, safe_filter, attributes=[ - "testAttr1", "testAttr2"]) + conn = ldap3.Connection(srv, user=safe_dn, auto_bind=True).search( + safe_dn, safe_filter) # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py index 011f3b82865..728a9179c7a 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py @@ -6,16 +6,10 @@ app = Flask(__name__) @app.route("/normal") def normal(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] + """ + A RemoteFlowSource is used directly as DN and search filter + """ - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter, ["testAttr1", "testAttr2"]) - - -@app.route("/normal_noAttrs") -def normal_noAttrs(): unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -26,30 +20,30 @@ def normal_noAttrs(): @app.route("/direct") def direct(): + """ + A RemoteFlowSource is used directly as DN and search filter using a oneline call to .search_s + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] user = ldap.initialize("ldap://127.0.0.1:1337").search_s( - unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter, ["testAttr1", "testAttr2"]) + unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter) @app.route("/normal_argbyname") def normal_argbyname(): + """ + A RemoteFlowSource is used directly as DN and search filter, while the search filter is specified as + an argument by name + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") user = ldap_connection.search_s( - unsafe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=unsafe_filter) - - -@app.route("/direct_argbyname") -def direct_argbyname(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] - - user = ldap.initialize("ldap://127.0.0.1:1337").search_s( - unsafe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=unsafe_filter) + unsafe_dn, ldap.SCOPE_SUBTREE, filterstr=unsafe_filter) # if __name__ == "__main__": diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py index eda3883da6d..b2c00cc04cb 100644 --- a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py +++ b/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py @@ -8,19 +8,10 @@ app = Flask(__name__) @app.route("/normal") def normal(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] + """ + A RemoteFlowSource is sanitized and used as DN and search filter + """ - safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) - safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) - - ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") - user = ldap_connection.search_s( - safe_dn, ldap.SCOPE_SUBTREE, safe_filter, ["testAttr1", "testAttr2"]) - - -@app.route("/normal_noAttrs") -def normal_noAttrs(): unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -34,6 +25,10 @@ def normal_noAttrs(): @app.route("/direct") def direct(): + """ + A RemoteFlowSource is sanitized and used as DN and search filter using a oneline call to .search_s + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -46,6 +41,11 @@ def direct(): @app.route("/normal_argbyname") def normal_argbyname(): + """ + A RemoteFlowSource is sanitized and used as DN and search filter, while the search filter is specified as + an argument by name + """ + unsafe_dn = "dc=%s" % request.args['dc'] unsafe_filter = "(user=%s)" % request.args['username'] @@ -54,19 +54,7 @@ def normal_argbyname(): ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") user = ldap_connection.search_s( - safe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=safe_filter) - - -@app.route("/direct_argbyname") -def direct_argbyname(): - unsafe_dn = "dc=%s" % request.args['dc'] - unsafe_filter = "(user=%s)" % request.args['username'] - - safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) - safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) - - user = ldap.initialize("ldap://127.0.0.1:1337").search_s( - safe_dn, ldap.SCOPE_SUBTREE, attrlist=["testAttr1", "testAttr2"], filterstr=safe_filter) + safe_dn, ldap.SCOPE_SUBTREE, filterstr=safe_filter) # if __name__ == "__main__": From 3c1ca7232469c71e96732e0fbbc4b8a825e3884a Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:44:30 +0200 Subject: [PATCH 022/550] Improve qhelp --- .../Security/CWE-090/LDAPInjection.qhelp | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp index a570549abfc..0c6c4b9ca20 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp @@ -1,28 +1,50 @@ - - + - -

    If an LDAP query is built by a not sanitized user-provided value, a user is likely to be able to run malicious LDAP queries.

    -
    + +

    If an LDAP query or DN is built using string concatenation or string formatting, and the +components of the concatenation include user input without any proper sanitization, a user +is likely to be able to run malicious LDAP queries.

    +
    - -

    In case user input must compose an LDAP query, it should be escaped in order to avoid a malicious user supplying special characters that change the actual purpose of the query. To do so, functions that ldap frameworks provide such as escape_filter_chars should be applied to that user input. - + +

    If user input must be included in an LDAP query or DN, it should be escaped to +avoid a malicious user providing special characters that change the meaning +of the query. In Python2, user input should be escaped with ldap.dn.escape_dn_chars +or ldap.filter.escape_filter_chars, while in Python3, user input should be escaped with +ldap3.utils.dn.escape_rdn or ldap3.utils.conv.escape_filter_chars +depending on the component tainted by the user. A good practice is to escape filter characters +that could change the meaning of the query (https://tools.ietf.org/search/rfc4515#section-3).

    +
    + +

    In the following examples, the code accepts both username and dc from the user, +which it then uses to build a LDAP query and DN.

    - -
  • - OWASP - LDAP Injection -
  • -
  • - SonarSource - RSPEC-2078 -
  • -
  • - Python - LDAP Documentation -
  • -
    +

    The first and the second example uses the unsanitized user input directly +in the search filter and DN for the LDAP query. +A malicious user could provide special characters to change the meaning of these +components, and search for a completely different set of values.

    + + + +

    In the third and four example, the input provided by the user is sanitized before it is included in the search filter or DN. +This ensures the meaning of the query cannot be changed by a malicious user.

    + + + +
    + + +
  • OWASP: LDAP Injection Prevention Cheat Sheet.
  • +
  • OWASP: LDAP Injection.
  • +
  • SonarSource: RSPEC-2078.
  • +
  • Python2: LDAP Documentation.
  • +
  • Python3: LDAP Documentation.
  • +
  • Wikipedia: LDAP injection.
  • +
  • BlackHat: LDAP Injection and Blind LDAP Injection.
  • +
  • LDAP: Understanding and Defending Against LDAP Injection Attacks.
  • +
    \ No newline at end of file From 1554f4f48d04e89c78783f2ec54c4f878fff60fe Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:44:46 +0200 Subject: [PATCH 023/550] Create qhelp examples --- .../Security/CWE-090/examples/example_bad1.py | 12 ++++++++++++ .../Security/CWE-090/examples/example_bad2.py | 12 ++++++++++++ .../Security/CWE-090/examples/example_good1.py | 17 +++++++++++++++++ .../Security/CWE-090/examples/example_good2.py | 17 +++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 python/ql/src/experimental/Security/CWE-090/examples/example_bad1.py create mode 100644 python/ql/src/experimental/Security/CWE-090/examples/example_bad2.py create mode 100644 python/ql/src/experimental/Security/CWE-090/examples/example_good1.py create mode 100644 python/ql/src/experimental/Security/CWE-090/examples/example_good2.py diff --git a/python/ql/src/experimental/Security/CWE-090/examples/example_bad1.py b/python/ql/src/experimental/Security/CWE-090/examples/example_bad1.py new file mode 100644 index 00000000000..4a1f86fd981 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/examples/example_bad1.py @@ -0,0 +1,12 @@ +from flask import request, Flask +import ldap + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + unsafe_dn, ldap.SCOPE_SUBTREE, unsafe_filter) diff --git a/python/ql/src/experimental/Security/CWE-090/examples/example_bad2.py b/python/ql/src/experimental/Security/CWE-090/examples/example_bad2.py new file mode 100644 index 00000000000..87cb4a65b8b --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/examples/example_bad2.py @@ -0,0 +1,12 @@ +from flask import request, Flask +import ldap3 + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=unsafe_dn, auto_bind=True) + conn.search(unsafe_dn, unsafe_filter) diff --git a/python/ql/src/experimental/Security/CWE-090/examples/example_good1.py b/python/ql/src/experimental/Security/CWE-090/examples/example_good1.py new file mode 100644 index 00000000000..ad9e9fe84f5 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/examples/example_good1.py @@ -0,0 +1,17 @@ +from flask import request, Flask +import ldap +import ldap.filter +import ldap.dn + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = ldap.dn.escape_dn_chars(unsafe_dn) + safe_filter = ldap.filter.escape_filter_chars(unsafe_filter) + + ldap_connection = ldap.initialize("ldap://127.0.0.1:1337") + user = ldap_connection.search_s( + safe_dn, ldap.SCOPE_SUBTREE, safe_filter) diff --git a/python/ql/src/experimental/Security/CWE-090/examples/example_good2.py b/python/ql/src/experimental/Security/CWE-090/examples/example_good2.py new file mode 100644 index 00000000000..c86db178040 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-090/examples/example_good2.py @@ -0,0 +1,17 @@ +from flask import request, Flask +import ldap3 +from ldap3.utils.dn import escape_rdn +from ldap3.utils.conv import escape_filter_chars + + +@app.route("/normal") +def normal(): + unsafe_dn = "dc=%s" % request.args['dc'] + unsafe_filter = "(user=%s)" % request.args['username'] + + safe_dn = escape_rdn(unsafe_dn) + safe_filter = escape_filter_chars(unsafe_filter) + + srv = ldap3.Server('ldap://127.0.0.1', port=1337) + conn = ldap3.Connection(srv, user=safe_dn, auto_bind=True) + conn.search(safe_dn, safe_filter) From 95bfdc49558f3fdee343fd9b018e45922a24678f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:45:03 +0200 Subject: [PATCH 024/550] Move tests to /test --- .../experimental/query-tests/Security/CWE-090}/ldap3_bad.py | 0 .../experimental/query-tests/Security/CWE-090}/ldap3_good.py | 0 .../experimental/query-tests/Security/CWE-090}/ldap_bad.py | 0 .../experimental/query-tests/Security/CWE-090}/ldap_good.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/{src/experimental/Security/CWE-090/unit_tests => test/experimental/query-tests/Security/CWE-090}/ldap3_bad.py (100%) rename python/ql/{src/experimental/Security/CWE-090/unit_tests => test/experimental/query-tests/Security/CWE-090}/ldap3_good.py (100%) rename python/ql/{src/experimental/Security/CWE-090/unit_tests => test/experimental/query-tests/Security/CWE-090}/ldap_bad.py (100%) rename python/ql/{src/experimental/Security/CWE-090/unit_tests => test/experimental/query-tests/Security/CWE-090}/ldap_good.py (100%) diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-090/ldap3_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_bad.py rename to python/ql/test/experimental/query-tests/Security/CWE-090/ldap3_bad.py diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py b/python/ql/test/experimental/query-tests/Security/CWE-090/ldap3_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/unit_tests/ldap3_good.py rename to python/ql/test/experimental/query-tests/Security/CWE-090/ldap3_good.py diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-090/ldap_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_bad.py rename to python/ql/test/experimental/query-tests/Security/CWE-090/ldap_bad.py diff --git a/python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py b/python/ql/test/experimental/query-tests/Security/CWE-090/ldap_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-090/unit_tests/ldap_good.py rename to python/ql/test/experimental/query-tests/Security/CWE-090/ldap_good.py From 4f85de87debf91ace22bdea6cffc4c5f9788c4c0 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:45:12 +0200 Subject: [PATCH 025/550] Add qlref --- .../query-tests/Security/CWE-090/LDAPInjection.qlref | 1 + 1 file changed, 1 insertion(+) create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.qlref diff --git a/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.qlref new file mode 100644 index 00000000000..98b37bfdcf6 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-090/LDAPInjection.ql From 7819d1a30b856d15c1ec3d7c051a0ec890d80b9f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:45:26 +0200 Subject: [PATCH 026/550] Generate .expected --- .../Security/CWE-090/LDAPInjection.expected | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.expected diff --git a/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.expected new file mode 100644 index 00000000000..07f46fbecdc --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security/CWE-090/LDAPInjection.expected @@ -0,0 +1,98 @@ +edges +| ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | ldap3_bad.py:13:27:13:38 | ControlFlowNode for Attribute | +| ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | ldap3_bad.py:14:35:14:41 | ControlFlowNode for request | +| ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | ldap3_bad.py:14:35:14:46 | ControlFlowNode for Attribute | +| ldap3_bad.py:13:27:13:38 | ControlFlowNode for Attribute | ldap3_bad.py:13:27:13:44 | ControlFlowNode for Subscript | +| ldap3_bad.py:13:27:13:44 | ControlFlowNode for Subscript | ldap3_bad.py:18:17:18:25 | ControlFlowNode for unsafe_dn | +| ldap3_bad.py:14:35:14:41 | ControlFlowNode for request | ldap3_bad.py:14:35:14:46 | ControlFlowNode for Attribute | +| ldap3_bad.py:14:35:14:46 | ControlFlowNode for Attribute | ldap3_bad.py:14:35:14:58 | ControlFlowNode for Subscript | +| ldap3_bad.py:14:35:14:58 | ControlFlowNode for Subscript | ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | +| ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | ldap3_bad.py:27:27:27:38 | ControlFlowNode for Attribute | +| ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | ldap3_bad.py:28:35:28:41 | ControlFlowNode for request | +| ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | ldap3_bad.py:28:35:28:46 | ControlFlowNode for Attribute | +| ldap3_bad.py:27:27:27:38 | ControlFlowNode for Attribute | ldap3_bad.py:27:27:27:44 | ControlFlowNode for Subscript | +| ldap3_bad.py:27:27:27:44 | ControlFlowNode for Subscript | ldap3_bad.py:32:9:32:17 | ControlFlowNode for unsafe_dn | +| ldap3_bad.py:28:35:28:41 | ControlFlowNode for request | ldap3_bad.py:28:35:28:46 | ControlFlowNode for Attribute | +| ldap3_bad.py:28:35:28:46 | ControlFlowNode for Attribute | ldap3_bad.py:28:35:28:58 | ControlFlowNode for Subscript | +| ldap3_bad.py:28:35:28:58 | ControlFlowNode for Subscript | ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | +| ldap_bad.py:13:27:13:33 | ControlFlowNode for request | ldap_bad.py:13:27:13:38 | ControlFlowNode for Attribute | +| ldap_bad.py:13:27:13:33 | ControlFlowNode for request | ldap_bad.py:14:35:14:41 | ControlFlowNode for request | +| ldap_bad.py:13:27:13:33 | ControlFlowNode for request | ldap_bad.py:14:35:14:46 | ControlFlowNode for Attribute | +| ldap_bad.py:13:27:13:38 | ControlFlowNode for Attribute | ldap_bad.py:13:27:13:44 | ControlFlowNode for Subscript | +| ldap_bad.py:13:27:13:44 | ControlFlowNode for Subscript | ldap_bad.py:18:9:18:17 | ControlFlowNode for unsafe_dn | +| ldap_bad.py:14:35:14:41 | ControlFlowNode for request | ldap_bad.py:14:35:14:46 | ControlFlowNode for Attribute | +| ldap_bad.py:14:35:14:46 | ControlFlowNode for Attribute | ldap_bad.py:14:35:14:58 | ControlFlowNode for Subscript | +| ldap_bad.py:14:35:14:58 | ControlFlowNode for Subscript | ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | +| ldap_bad.py:27:27:27:33 | ControlFlowNode for request | ldap_bad.py:27:27:27:38 | ControlFlowNode for Attribute | +| ldap_bad.py:27:27:27:33 | ControlFlowNode for request | ldap_bad.py:28:35:28:41 | ControlFlowNode for request | +| ldap_bad.py:27:27:27:33 | ControlFlowNode for request | ldap_bad.py:28:35:28:46 | ControlFlowNode for Attribute | +| ldap_bad.py:27:27:27:38 | ControlFlowNode for Attribute | ldap_bad.py:27:27:27:44 | ControlFlowNode for Subscript | +| ldap_bad.py:27:27:27:44 | ControlFlowNode for Subscript | ldap_bad.py:31:9:31:17 | ControlFlowNode for unsafe_dn | +| ldap_bad.py:28:35:28:41 | ControlFlowNode for request | ldap_bad.py:28:35:28:46 | ControlFlowNode for Attribute | +| ldap_bad.py:28:35:28:46 | ControlFlowNode for Attribute | ldap_bad.py:28:35:28:58 | ControlFlowNode for Subscript | +| ldap_bad.py:28:35:28:58 | ControlFlowNode for Subscript | ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | +| ldap_bad.py:41:27:41:33 | ControlFlowNode for request | ldap_bad.py:41:27:41:38 | ControlFlowNode for Attribute | +| ldap_bad.py:41:27:41:33 | ControlFlowNode for request | ldap_bad.py:42:35:42:41 | ControlFlowNode for request | +| ldap_bad.py:41:27:41:33 | ControlFlowNode for request | ldap_bad.py:42:35:42:46 | ControlFlowNode for Attribute | +| ldap_bad.py:41:27:41:38 | ControlFlowNode for Attribute | ldap_bad.py:41:27:41:44 | ControlFlowNode for Subscript | +| ldap_bad.py:41:27:41:44 | ControlFlowNode for Subscript | ldap_bad.py:46:9:46:17 | ControlFlowNode for unsafe_dn | +| ldap_bad.py:42:35:42:41 | ControlFlowNode for request | ldap_bad.py:42:35:42:46 | ControlFlowNode for Attribute | +| ldap_bad.py:42:35:42:46 | ControlFlowNode for Attribute | ldap_bad.py:42:35:42:58 | ControlFlowNode for Subscript | +| ldap_bad.py:42:35:42:58 | ControlFlowNode for Subscript | ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | +nodes +| ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap3_bad.py:13:27:13:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap3_bad.py:13:27:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap3_bad.py:14:35:14:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap3_bad.py:14:35:14:46 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap3_bad.py:14:35:14:58 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap3_bad.py:18:17:18:25 | ControlFlowNode for unsafe_dn | semmle.label | ControlFlowNode for unsafe_dn | +| ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | semmle.label | ControlFlowNode for unsafe_filter | +| ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap3_bad.py:27:27:27:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap3_bad.py:27:27:27:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap3_bad.py:28:35:28:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap3_bad.py:28:35:28:46 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap3_bad.py:28:35:28:58 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap3_bad.py:32:9:32:17 | ControlFlowNode for unsafe_dn | semmle.label | ControlFlowNode for unsafe_dn | +| ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | semmle.label | ControlFlowNode for unsafe_filter | +| ldap_bad.py:13:27:13:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:13:27:13:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:13:27:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:14:35:14:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:14:35:14:46 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:14:35:14:58 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:18:9:18:17 | ControlFlowNode for unsafe_dn | semmle.label | ControlFlowNode for unsafe_dn | +| ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | semmle.label | ControlFlowNode for unsafe_filter | +| ldap_bad.py:27:27:27:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:27:27:27:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:27:27:27:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:28:35:28:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:28:35:28:46 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:28:35:28:58 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:31:9:31:17 | ControlFlowNode for unsafe_dn | semmle.label | ControlFlowNode for unsafe_dn | +| ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | semmle.label | ControlFlowNode for unsafe_filter | +| ldap_bad.py:41:27:41:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:41:27:41:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:41:27:41:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:42:35:42:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| ldap_bad.py:42:35:42:46 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| ldap_bad.py:42:35:42:58 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| ldap_bad.py:46:9:46:17 | ControlFlowNode for unsafe_dn | semmle.label | ControlFlowNode for unsafe_dn | +| ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | semmle.label | ControlFlowNode for unsafe_filter | +#select +| ldap3_bad.py:18:17:18:25 | ControlFlowNode for unsafe_dn | ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | ldap3_bad.py:18:17:18:25 | ControlFlowNode for unsafe_dn | $@ LDAP query parameter comes from $@. | ldap3_bad.py:18:17:18:25 | ControlFlowNode for unsafe_dn | This | ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | a user-provided value | +| ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | This | ldap3_bad.py:13:27:13:33 | ControlFlowNode for request | a user-provided value | +| ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | ldap3_bad.py:14:35:14:41 | ControlFlowNode for request | ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:18:28:18:40 | ControlFlowNode for unsafe_filter | This | ldap3_bad.py:14:35:14:41 | ControlFlowNode for request | a user-provided value | +| ldap3_bad.py:32:9:32:17 | ControlFlowNode for unsafe_dn | ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | ldap3_bad.py:32:9:32:17 | ControlFlowNode for unsafe_dn | $@ LDAP query parameter comes from $@. | ldap3_bad.py:32:9:32:17 | ControlFlowNode for unsafe_dn | This | ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | a user-provided value | +| ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | This | ldap3_bad.py:27:27:27:33 | ControlFlowNode for request | a user-provided value | +| ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | ldap3_bad.py:28:35:28:41 | ControlFlowNode for request | ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:32:20:32:32 | ControlFlowNode for unsafe_filter | This | ldap3_bad.py:28:35:28:41 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:18:9:18:17 | ControlFlowNode for unsafe_dn | ldap_bad.py:13:27:13:33 | ControlFlowNode for request | ldap_bad.py:18:9:18:17 | ControlFlowNode for unsafe_dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:18:9:18:17 | ControlFlowNode for unsafe_dn | This | ldap_bad.py:13:27:13:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | ldap_bad.py:13:27:13:33 | ControlFlowNode for request | ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:13:27:13:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | ldap_bad.py:14:35:14:41 | ControlFlowNode for request | ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:18:40:18:52 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:14:35:14:41 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:31:9:31:17 | ControlFlowNode for unsafe_dn | ldap_bad.py:27:27:27:33 | ControlFlowNode for request | ldap_bad.py:31:9:31:17 | ControlFlowNode for unsafe_dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:31:9:31:17 | ControlFlowNode for unsafe_dn | This | ldap_bad.py:27:27:27:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | ldap_bad.py:27:27:27:33 | ControlFlowNode for request | ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:27:27:27:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | ldap_bad.py:28:35:28:41 | ControlFlowNode for request | ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:31:40:31:52 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:28:35:28:41 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:46:9:46:17 | ControlFlowNode for unsafe_dn | ldap_bad.py:41:27:41:33 | ControlFlowNode for request | ldap_bad.py:46:9:46:17 | ControlFlowNode for unsafe_dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:46:9:46:17 | ControlFlowNode for unsafe_dn | This | ldap_bad.py:41:27:41:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | ldap_bad.py:41:27:41:33 | ControlFlowNode for request | ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:41:27:41:33 | ControlFlowNode for request | a user-provided value | +| ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | ldap_bad.py:42:35:42:41 | ControlFlowNode for request | ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:46:50:46:62 | ControlFlowNode for unsafe_filter | This | ldap_bad.py:42:35:42:41 | ControlFlowNode for request | a user-provided value | From b405c675c2bb7a62faac778b968b827ef9b8b9f7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:49:33 +0200 Subject: [PATCH 027/550] Add qhelp last newline --- python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp index 0c6c4b9ca20..e3b47f23b0f 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp @@ -47,4 +47,4 @@ This ensures the meaning of the query cannot be changed by a malicious user.

  • BlackHat: LDAP Injection and Blind LDAP Injection.
  • LDAP: Understanding and Defending Against LDAP Injection Attacks.
  • - \ No newline at end of file + From 82f47f85715f8d92839b820ccb5bd4b8cc48b41c Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 23:55:34 +0200 Subject: [PATCH 028/550] Polish metadata --- .../ql/src/experimental/Security/CWE-090/LDAPInjection.ql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql index 28748595c46..50c89248318 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.ql @@ -1,9 +1,10 @@ /** - * @name Python LDAP Injection - * @description Python LDAP Injection through search filter + * @name LDAP query built from user-controlled sources + * @description Building an LDAP query from user-controlled sources is vulnerable to insertion of + * malicious LDAP code by the user. * @kind path-problem * @problem.severity error - * @id python/ldap-injection + * @id py/ldap-injection * @tags experimental * security * external/cwe/cwe-090 From cd75433e3945fd6ffa4166d4c09108b489e37587 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 9 Apr 2021 00:52:50 +0200 Subject: [PATCH 029/550] Fix qhelp examples extension --- .../src/experimental/Security/CWE-090/LDAPInjection.qhelp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp index e3b47f23b0f..bda9b75da99 100644 --- a/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-090/LDAPInjection.qhelp @@ -27,14 +27,14 @@ in the search filter and DN for the LDAP query. A malicious user could provide special characters to change the meaning of these components, and search for a completely different set of values.

    - - + +

    In the third and four example, the input provided by the user is sanitized before it is included in the search filter or DN. This ensures the meaning of the query cannot be changed by a malicious user.

    - - + + From a2e8d88a07df14cf22bbca4e3039b54107f8983a Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 9 Apr 2021 01:47:44 +0200 Subject: [PATCH 030/550] Write documentation --- .../experimental/semmle/python/Concepts.qll | 32 +++++++++++++ .../semmle/python/frameworks/Stdlib.qll | 46 +++++++++++++++++++ .../semmle/python/security/injection/LDAP.qll | 2 +- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 9022f9c3818..9d0dddc3f14 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -14,12 +14,28 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks +/** Provides classes for modeling LDAP-related APIs. */ module LDAPQuery { + /** + * A data-flow node that collects methods executing a LDAP query. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `LDAPQuery` instead. + */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the executed expression. + */ abstract DataFlow::Node getLDAPNode(); } } +/** + * A data-flow node that collect methods executing a LDAP query. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `LDAPQuery::Range` instead. + */ class LDAPQuery extends DataFlow::Node { LDAPQuery::Range range; @@ -28,12 +44,28 @@ class LDAPQuery extends DataFlow::Node { DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } } +/** Provides classes for modeling LDAP components escape-related APIs. */ module LDAPEscape { + /** + * A data-flow node that collects functions escaping LDAP components. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `LDAPEscape` instead. + */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the escaped expression. + */ abstract DataFlow::Node getEscapeNode(); } } +/** + * A data-flow node that collects functions escaping LDAP components. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexEscape::Range` instead. + */ class LDAPEscape extends DataFlow::Node { LDAPEscape::Range range; diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 76d4f326878..2bbca55f820 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -10,14 +10,32 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs +/** + * Provides models for Python's ldap-related libraries. + */ private module LDAP { + /** + * Provides models for Python's `ldap` library. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html + */ private module LDAP2 { + /** + * List of `ldap` methods used to execute a query. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions + */ private class LDAP2QueryMethods extends string { LDAP2QueryMethods() { this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"] } } + /** + * A class to find `ldap` methods executing a query. + * + * See `LDAP2QueryMethods` + */ private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; @@ -41,6 +59,11 @@ private module LDAP { override DataFlow::Node getLDAPNode() { result = ldapNode } } + /** + * A class to find calls to `ldap.dn.escape_dn_chars`. + * + * See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17 + */ private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { LDAP2EscapeDNCall() { this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() @@ -49,6 +72,11 @@ private module LDAP { override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } + /** + * A class to find calls to `ldap.filter.escape_filter_chars`. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars + */ private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { LDAP2EscapeFilterCall() { this = @@ -59,7 +87,15 @@ private module LDAP { } } + /** + * Provides models for Python's `ldap3` library. + * + * See https://pypi.org/project/ldap3/ + */ private module LDAP3 { + /** + * A class to find `ldap3` methods executing a query. + */ private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { DataFlow::Node ldapNode; @@ -79,6 +115,11 @@ private module LDAP { override DataFlow::Node getLDAPNode() { result = ldapNode } } + /** + * A class to find calls to `ldap3.utils.dn.escape_rdn`. + * + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390 + */ private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { LDAP3EscapeDNCall() { this = @@ -92,6 +133,11 @@ private module LDAP { override DataFlow::Node getEscapeNode() { result = this.getArg(0) } } + /** + * A class to find calls to `ldap3.utils.conv.escape_filter_chars`. + * + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91 + */ private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { LDAP3EscapeFilterCall() { this = diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll index febebe0a8fd..9f91f91b321 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll @@ -9,7 +9,7 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources /** - * A taint-tracking configuration for detecting regular expression injections. + * A taint-tracking configuration for detecting LDAP injections. */ class LDAPInjectionFlowConfig extends TaintTracking::Configuration { LDAPInjectionFlowConfig() { this = "LDAPInjectionFlowConfig" } From b020ea6e3aef0c0716cb931d9e22bd4f48c5bd18 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 9 Apr 2021 01:50:23 +0200 Subject: [PATCH 031/550] Polish documentation --- python/ql/src/experimental/semmle/python/Concepts.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 9d0dddc3f14..89a82fff87c 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -14,7 +14,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -/** Provides classes for modeling LDAP-related APIs. */ +/** Provides classes for modeling LDAP query execution-related APIs. */ module LDAPQuery { /** * A data-flow node that collects methods executing a LDAP query. From 1c34230efbab719f332e1feb35ef7460edc3ae39 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 9 Apr 2021 01:58:18 +0200 Subject: [PATCH 032/550] Fix documentation typo --- python/ql/src/experimental/semmle/python/Concepts.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 89a82fff87c..cb90a465a54 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -64,7 +64,7 @@ module LDAPEscape { * A data-flow node that collects functions escaping LDAP components. * * Extend this class to refine existing API models. If you want to model new APIs, - * extend `RegexEscape::Range` instead. + * extend `LDAPEscape::Range` instead. */ class LDAPEscape extends DataFlow::Node { LDAPEscape::Range range; From 3b437fe6cf1e4d9cee9b807869ddd0272e35d732 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 8 Apr 2021 15:52:41 +0100 Subject: [PATCH 033/550] C++: Replace GVN with some other libraries. --- ...nsignedDifferenceExpressionComparedZero.ql | 20 +++++++++++++++---- ...dDifferenceExpressionComparedZero.expected | 7 ------- .../test.cpp | 14 ++++++------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index bca77f99a72..cbdae0b6651 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -12,10 +12,10 @@ import cpp import semmle.code.cpp.commons.Exclusions -import semmle.code.cpp.valuenumbering.GlobalValueNumbering import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.dataflow.DataFlow /** * Holds if `sub` is guarded by a condition which ensures that @@ -37,15 +37,27 @@ predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { e = sub.getLeftOperand() or exists(Expr other | - // GVN equality + // use-use exprIsSubLeftOrLess(sub, other) and - globalValueNumber(e) = globalValueNumber(other) + ( + useUsePair(_, other, e) or + useUsePair(_, e, other) + ) + ) + or + exists(Expr other | + // dataflow + exprIsSubLeftOrLess(sub, other) and + ( + DataFlow::localFlowStep(DataFlow::exprNode(e), DataFlow::exprNode(other)) or + DataFlow::localFlowStep(DataFlow::exprNode(other), DataFlow::exprNode(e)) + ) ) or exists(Expr other | // guard constraining `sub` exprIsSubLeftOrLess(sub, other) and - isGuarded(sub, other, e) // left >= right + isGuarded(sub, other, e) // other >= e ) or exists(Expr other, float p, float q | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 64a1aa0ab89..85938f15499 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -1,12 +1,6 @@ | test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:15:9:15:25 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:39:12:39:20 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:47:5:47:13 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:55:5:55:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. | @@ -14,7 +8,6 @@ | test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:195:6:195:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index f151c593a2d..1a6aaa618e5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -12,7 +12,7 @@ void test(unsigned x, unsigned y, bool unknown) { } if(total <= limit) { - while(limit - total > 0) { // GOOD [FALSE POSITIVE] + while(limit - total > 0) { // GOOD total += getAnInt(); if(total > limit) break; } @@ -29,14 +29,14 @@ void test(unsigned x, unsigned y, bool unknown) { } else { y = x; } - bool b1 = x - y > 0; // GOOD [FALSE POSITIVE] + bool b1 = x - y > 0; // GOOD x = getAnInt(); y = getAnInt(); if(y > x) { y = x - 1; } - bool b2 = x - y > 0; // GOOD [FALSE POSITIVE] + bool b2 = x - y > 0; // GOOD int N = getAnInt(); y = x; @@ -44,7 +44,7 @@ void test(unsigned x, unsigned y, bool unknown) { if(unknown) { y--; } } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD x = y; while(cond()) { @@ -52,7 +52,7 @@ void test(unsigned x, unsigned y, bool unknown) { y--; } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD y = 0; for(int i = 0; i < x; ++i) { @@ -66,7 +66,7 @@ void test(unsigned x, unsigned y, bool unknown) { if(unknown) { x++; } } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD int n = getAnInt(); if (n > x - y) { n = x - y; } @@ -192,7 +192,7 @@ void test10() { a = b; } - if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + if (a - b > 0) { // GOOD (as a >= b) // ... } } From 0818c1d7038a84c39a04d2282f023d56ff280f4a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 9 Apr 2021 18:11:48 +0100 Subject: [PATCH 034/550] C++: Update QLDoc. --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index cbdae0b6651..074ecfc821e 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -31,7 +31,8 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { } /** - * Holds if `e` is known to be less than or equal to `sub.getLeftOperand()`. + * Holds if `e` is known or suspected to be less than or equal to + * `sub.getLeftOperand()`. */ predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { e = sub.getLeftOperand() From 40637c18ceb1684c16db25f2e59275497f287871 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 9 Apr 2021 18:14:12 +0100 Subject: [PATCH 035/550] C++: Add change note. --- .../2021-04-09-unsigned-difference-expression-compared-zero.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md diff --git a/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md new file mode 100644 index 00000000000..3297dfce9a9 --- /dev/null +++ b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Unsigned difference expression compared to zero' (cpp/unsigned-difference-expression-compared-zero) query has been improved to produce fewer false positive results. \ No newline at end of file From b0ad927fddd2a874f7b08acd6fc89e7cc44650a6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 13 Apr 2021 15:03:06 +0100 Subject: [PATCH 036/550] C++: Remove useUsePair. --- .../CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 9 --------- .../UnsignedDifferenceExpressionComparedZero.expected | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 074ecfc821e..3e2d49f4987 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -37,15 +37,6 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { e = sub.getLeftOperand() or - exists(Expr other | - // use-use - exprIsSubLeftOrLess(sub, other) and - ( - useUsePair(_, other, e) or - useUsePair(_, e, other) - ) - ) - or exists(Expr other | // dataflow exprIsSubLeftOrLess(sub, other) and diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 85938f15499..7f03b7bcdc7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -1,5 +1,7 @@ | test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:22:12:22:20 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | @@ -9,6 +11,8 @@ | test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:219:7:219:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:226:8:226:16 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:276:11:276:19 | ... > ... | Unsigned subtraction can never be negative. | From 4879104568879fe39c6c1c324f6e91f3df40c41a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 13 Apr 2021 14:50:13 +0100 Subject: [PATCH 037/550] C++: Add more dataflow cases to replace the loss. --- ...nsignedDifferenceExpressionComparedZero.ql | 20 ++++++++++++++++++- ...dDifferenceExpressionComparedZero.expected | 4 ---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 3e2d49f4987..682341ed4b2 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -34,7 +34,7 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { * Holds if `e` is known or suspected to be less than or equal to * `sub.getLeftOperand()`. */ -predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { +predicate exprIsSubLeftOrLess(SubExpr sub, Element e) { e = sub.getLeftOperand() or exists(Expr other | @@ -46,6 +46,24 @@ predicate exprIsSubLeftOrLess(SubExpr sub, Expr e) { ) ) or + exists(Element other | + // dataflow (via parameter) + exprIsSubLeftOrLess(sub, other) and + ( + DataFlow::localFlowStep(DataFlow::parameterNode(e), DataFlow::exprNode(other)) or + DataFlow::localFlowStep(DataFlow::parameterNode(other), DataFlow::exprNode(e)) + ) + ) + or + exists(Element other | + // dataflow (via uninitialized) + exprIsSubLeftOrLess(sub, other) and + ( + DataFlow::localFlowStep(DataFlow::uninitializedNode(e), DataFlow::exprNode(other)) or + DataFlow::localFlowStep(DataFlow::uninitializedNode(other), DataFlow::exprNode(e)) + ) + ) + or exists(Expr other | // guard constraining `sub` exprIsSubLeftOrLess(sub, other) and diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 7f03b7bcdc7..85938f15499 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -1,7 +1,5 @@ | test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:22:12:22:20 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | @@ -11,8 +9,6 @@ | test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:219:7:219:15 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:226:8:226:16 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:276:11:276:19 | ... > ... | Unsigned subtraction can never be negative. | From 8daca01c87336be802e4470d84ee0de676cdd998 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 13 Apr 2021 15:13:11 +0100 Subject: [PATCH 038/550] C++: Cleaner use of DataFlow::Node in exprIsSubLeftOrLess. --- ...nsignedDifferenceExpressionComparedZero.ql | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 682341ed4b2..b002cd3631e 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -31,57 +31,39 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { } /** - * Holds if `e` is known or suspected to be less than or equal to + * Holds if `n` is known or suspected to be less than or equal to * `sub.getLeftOperand()`. */ -predicate exprIsSubLeftOrLess(SubExpr sub, Element e) { - e = sub.getLeftOperand() +predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { + n = DataFlow::exprNode(sub.getLeftOperand()) or - exists(Expr other | + exists(DataFlow::Node other | // dataflow exprIsSubLeftOrLess(sub, other) and ( - DataFlow::localFlowStep(DataFlow::exprNode(e), DataFlow::exprNode(other)) or - DataFlow::localFlowStep(DataFlow::exprNode(other), DataFlow::exprNode(e)) + DataFlow::localFlowStep(n, other) or + DataFlow::localFlowStep(other, n) ) ) or - exists(Element other | - // dataflow (via parameter) - exprIsSubLeftOrLess(sub, other) and - ( - DataFlow::localFlowStep(DataFlow::parameterNode(e), DataFlow::exprNode(other)) or - DataFlow::localFlowStep(DataFlow::parameterNode(other), DataFlow::exprNode(e)) - ) - ) - or - exists(Element other | - // dataflow (via uninitialized) - exprIsSubLeftOrLess(sub, other) and - ( - DataFlow::localFlowStep(DataFlow::uninitializedNode(e), DataFlow::exprNode(other)) or - DataFlow::localFlowStep(DataFlow::uninitializedNode(other), DataFlow::exprNode(e)) - ) - ) - or - exists(Expr other | + exists(DataFlow::Node other | // guard constraining `sub` exprIsSubLeftOrLess(sub, other) and - isGuarded(sub, other, e) // other >= e + isGuarded(sub, other.asExpr(), n.asExpr()) // other >= e ) or - exists(Expr other, float p, float q | + exists(DataFlow::Node other, float p, float q | // linear access of `other` exprIsSubLeftOrLess(sub, other) and - linearAccess(e, other, p, q) and // e = p * other + q + linearAccess(n.asExpr(), other.asExpr(), p, q) and // e = p * other + q p <= 1 and q <= 0 ) or - exists(Expr other, float p, float q | + exists(DataFlow::Node other, float p, float q | // linear access of `e` exprIsSubLeftOrLess(sub, other) and - linearAccess(other, e, p, q) and // other = p * e + q + linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * e + q p >= 1 and q >= 0 ) @@ -95,5 +77,5 @@ where ro.getGreaterOperand() = sub and sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants - not exprIsSubLeftOrLess(sub, sub.getRightOperand()) // generally catches false positives where there's a relation between the left and right operands + not exprIsSubLeftOrLess(sub, DataFlow::exprNode(sub.getRightOperand())) // generally catches false positives where there's a relation between the left and right operands select ro, "Unsigned subtraction can never be negative." From 29e320627fcf1e34126f0070e021b4edd9e5a72a Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Fri, 16 Apr 2021 23:19:29 +0300 Subject: [PATCH 039/550] Regex injection --- .../Security/CWE/CWE-730/RegexInjection.java | 38 ++++++++ .../Security/CWE/CWE-730/RegexInjection.qhelp | 48 +++++++++++ .../Security/CWE/CWE-730/RegexInjection.ql | 72 ++++++++++++++++ .../security/CWE-730/RegexInjection.expected | 39 +++++++++ .../security/CWE-730/RegexInjection.java | 86 +++++++++++++++++++ .../security/CWE-730/RegexInjection.qlref | 1 + .../query-tests/security/CWE-730/options | 1 + 7 files changed, 285 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-730/options diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java new file mode 100644 index 00000000000..387648a443e --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java @@ -0,0 +1,38 @@ +package com.example.demo; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DemoApplication { + + @GetMapping("/string1") + public String string1(@RequestParam(value = "input", defaultValue = "test") String input, + @RequestParam(value = "pattern", defaultValue = ".*") String pattern) { + // BAD: Unsanitized user input is used to construct a regular expression + if (input.matches("^" + pattern + "=.*$")) + return "match!"; + + return "doesn't match!"; + } + + @GetMapping("/string2") + public String string2(@RequestParam(value = "input", defaultValue = "test") String input, + @RequestParam(value = "pattern", defaultValue = ".*") String pattern) { + // GOOD: User input is sanitized before constructing the regex + if (input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$")) + return "match!"; + + return "doesn't match!"; + } + + Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]"); + + String escapeSpecialRegexChars(String str) { + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0"); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp new file mode 100644 index 00000000000..6cd80b52f75 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp @@ -0,0 +1,48 @@ + + + + +

    +Constructing a regular expression with unsanitized user input is dangerous as a malicious user may +be able to modify the meaning of the expression. In particular, such a user may be able to provide +a regular expression fragment that takes exponential time in the worst case, and use that to +perform a Denial of Service attack. +

    +
    + + +

    +Before embedding user input into a regular expression, use a sanitization function +to escape meta-characters that have special meaning. +

    +
    + + +

    +The following example shows a HTTP request parameter that is used to construct a regular expression: +

    + +

    +In the first case the user-provided regex is not escaped. +If a malicious user provides a regex that has exponential worst case performance, +then this could lead to a Denial of Service. +

    +

    +In the second case, the user input is escaped using escapeSpecialRegexChars before being included +in the regular expression. This ensures that the user cannot insert characters which have a special +meaning in regular expressions. +

    +
    + + +
  • +OWASP: +Regular expression Denial of Service - ReDoS. +
  • +
  • +Wikipedia: ReDoS. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql new file mode 100644 index 00000000000..58d0d81d615 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -0,0 +1,72 @@ +/** + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being sanitized, + * otherwise a malicious user may be able to provide a regex that could require + * exponential time on certain inputs. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/regex-injection + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking +import DataFlow::PathGraph + +class RegexSink extends DataFlow::ExprNode { + RegexSink() { + exists(MethodAccess ma, Method m | m = ma.getMethod() | + ( + ma.getArgument(0) = this.asExpr() and + ( + m.getDeclaringType().hasQualifiedName("java.lang", "String") and + ( + m.hasName("matches") or + m.hasName("split") or + m.hasName("replaceFirst") or + m.hasName("replaceAll") + ) + or + m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and + ( + m.hasName("compile") or + m.hasName("matches") + ) + ) + ) + ) + } +} + +abstract class Sanitizer extends DataFlow::ExprNode { } + +class RegExpSanitizationCall extends Sanitizer { + RegExpSanitizationCall() { + exists(string calleeName, string sanitize, string regexp | + calleeName = this.asExpr().(Call).getCallee().getName() and + sanitize = "(?:escape|saniti[sz]e)" and + regexp = "regexp?" + | + calleeName.regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + ".*)") + ) + } +} + +class RegexInjectionConfiguration extends TaintTracking::Configuration { + RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, RegexInjectionConfiguration c +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ is user controlled.", source.getNode(), + "This regular expression pattern" diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected new file mode 100644 index 00000000000..521a45d3d47 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected @@ -0,0 +1,39 @@ +edges +| RegexInjection.java:11:22:11:52 | getParameter(...) : String | RegexInjection.java:14:26:14:47 | ... + ... | +| RegexInjection.java:18:22:18:52 | getParameter(...) : String | RegexInjection.java:21:24:21:30 | pattern | +| RegexInjection.java:25:22:25:52 | getParameter(...) : String | RegexInjection.java:28:31:28:37 | pattern | +| RegexInjection.java:32:22:32:52 | getParameter(...) : String | RegexInjection.java:35:29:35:35 | pattern | +| RegexInjection.java:39:22:39:52 | getParameter(...) : String | RegexInjection.java:42:34:42:40 | pattern | +| RegexInjection.java:49:22:49:52 | getParameter(...) : String | RegexInjection.java:52:28:52:34 | pattern | +| RegexInjection.java:56:22:56:52 | getParameter(...) : String | RegexInjection.java:59:28:59:34 | pattern | +| RegexInjection.java:63:22:63:52 | getParameter(...) : String | RegexInjection.java:66:36:66:42 | pattern : String | +| RegexInjection.java:66:32:66:43 | foo(...) : String | RegexInjection.java:66:26:66:52 | ... + ... | +| RegexInjection.java:66:36:66:42 | pattern : String | RegexInjection.java:66:32:66:43 | foo(...) : String | +nodes +| RegexInjection.java:11:22:11:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:14:26:14:47 | ... + ... | semmle.label | ... + ... | +| RegexInjection.java:18:22:18:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:21:24:21:30 | pattern | semmle.label | pattern | +| RegexInjection.java:25:22:25:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:28:31:28:37 | pattern | semmle.label | pattern | +| RegexInjection.java:32:22:32:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:35:29:35:35 | pattern | semmle.label | pattern | +| RegexInjection.java:39:22:39:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:42:34:42:40 | pattern | semmle.label | pattern | +| RegexInjection.java:49:22:49:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:52:28:52:34 | pattern | semmle.label | pattern | +| RegexInjection.java:56:22:56:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:59:28:59:34 | pattern | semmle.label | pattern | +| RegexInjection.java:63:22:63:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:66:26:66:52 | ... + ... | semmle.label | ... + ... | +| RegexInjection.java:66:32:66:43 | foo(...) : String | semmle.label | foo(...) : String | +| RegexInjection.java:66:36:66:42 | pattern : String | semmle.label | pattern : String | +#select +| RegexInjection.java:14:26:14:47 | ... + ... | RegexInjection.java:11:22:11:52 | getParameter(...) : String | RegexInjection.java:14:26:14:47 | ... + ... | $@ is user controlled. | RegexInjection.java:11:22:11:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:21:24:21:30 | pattern | RegexInjection.java:18:22:18:52 | getParameter(...) : String | RegexInjection.java:21:24:21:30 | pattern | $@ is user controlled. | RegexInjection.java:18:22:18:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:28:31:28:37 | pattern | RegexInjection.java:25:22:25:52 | getParameter(...) : String | RegexInjection.java:28:31:28:37 | pattern | $@ is user controlled. | RegexInjection.java:25:22:25:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:35:29:35:35 | pattern | RegexInjection.java:32:22:32:52 | getParameter(...) : String | RegexInjection.java:35:29:35:35 | pattern | $@ is user controlled. | RegexInjection.java:32:22:32:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:42:34:42:40 | pattern | RegexInjection.java:39:22:39:52 | getParameter(...) : String | RegexInjection.java:42:34:42:40 | pattern | $@ is user controlled. | RegexInjection.java:39:22:39:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:52:28:52:34 | pattern | RegexInjection.java:49:22:49:52 | getParameter(...) : String | RegexInjection.java:52:28:52:34 | pattern | $@ is user controlled. | RegexInjection.java:49:22:49:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:59:28:59:34 | pattern | RegexInjection.java:56:22:56:52 | getParameter(...) : String | RegexInjection.java:59:28:59:34 | pattern | $@ is user controlled. | RegexInjection.java:56:22:56:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:66:26:66:52 | ... + ... | RegexInjection.java:63:22:63:52 | getParameter(...) : String | RegexInjection.java:66:26:66:52 | ... + ... | $@ is user controlled. | RegexInjection.java:63:22:63:52 | getParameter(...) | This regular expression pattern | diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java new file mode 100644 index 00000000000..c203360e105 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java @@ -0,0 +1,86 @@ +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; + +public class RegexInjection extends HttpServlet { + public boolean string1(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return input.matches("^" + pattern + "=.*$"); // BAD + } + + public boolean string2(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return input.split(pattern).length > 0; // BAD + } + + public boolean string3(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return input.replaceFirst(pattern, "").length() > 0; // BAD + } + + public boolean string4(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return input.replaceAll(pattern, "").length() > 0; // BAD + } + + public boolean pattern1(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + Pattern pt = Pattern.compile(pattern); + Matcher matcher = pt.matcher(input); + + return matcher.find(); // BAD + } + + public boolean pattern2(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return Pattern.compile(pattern).matcher(input).matches(); // BAD + } + + public boolean pattern3(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return Pattern.matches(pattern, input); // BAD + } + + public boolean pattern4(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return input.matches("^" + foo(pattern) + "=.*$"); // BAD + } + + String foo(String str) { + return str; + } + + public boolean pattern5(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + // GOOD: User input is sanitized before constructing the regex + return input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$"); + } + + Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]"); + + String escapeSpecialRegexChars(String str) { + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0"); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.qlref new file mode 100644 index 00000000000..dca594b38d2 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-730/RegexInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/options b/java/ql/test/experimental/query-tests/security/CWE-730/options new file mode 100644 index 00000000000..dd125ad0f4e --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-730/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4 \ No newline at end of file From 9f8a9b9cadb8f1811e52f0de523467058629c54c Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 20 Apr 2021 17:10:09 +0100 Subject: [PATCH 040/550] JS: Add taint source/sink summary queries --- javascript/ql/src/Summary/TaintSinks.ql | 15 +++++++++++++++ javascript/ql/src/Summary/TaintSources.ql | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 javascript/ql/src/Summary/TaintSinks.ql create mode 100644 javascript/ql/src/Summary/TaintSources.ql diff --git a/javascript/ql/src/Summary/TaintSinks.ql b/javascript/ql/src/Summary/TaintSinks.ql new file mode 100644 index 00000000000..24f6579efec --- /dev/null +++ b/javascript/ql/src/Summary/TaintSinks.ql @@ -0,0 +1,15 @@ +/** + * @name Taint sinks + * @description Expressions that are vulnerable if containing untrusted data. + * @kind problem + * @problem.severity informational + * @id js/summary/taint-sinks + * @tags summary + * @precision medium + */ + +import javascript +import meta.internal.TaintMetrics + +from string kind +select relevantTaintSink(kind), kind + " sink" diff --git a/javascript/ql/src/Summary/TaintSources.ql b/javascript/ql/src/Summary/TaintSources.ql new file mode 100644 index 00000000000..7178a76dde9 --- /dev/null +++ b/javascript/ql/src/Summary/TaintSources.ql @@ -0,0 +1,16 @@ +/** + * @name Taint sources + * @description Sources of untrusted input. + * @kind problem + * @problem.severity informational + * @id js/summary/taint-sources + * @tags summary + * @precision medium + */ + +import javascript +import meta.internal.TaintMetrics + +from RemoteFlowSource node +where node = relevantTaintSource() +select node, node.getSourceType() From 02707f0777d42601511f3ad6cd22927baed41fae Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 20 Apr 2021 19:51:16 +0100 Subject: [PATCH 041/550] JS: informational -> info --- javascript/ql/src/Summary/TaintSinks.ql | 2 +- javascript/ql/src/Summary/TaintSources.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/Summary/TaintSinks.ql b/javascript/ql/src/Summary/TaintSinks.ql index 24f6579efec..2da65398935 100644 --- a/javascript/ql/src/Summary/TaintSinks.ql +++ b/javascript/ql/src/Summary/TaintSinks.ql @@ -2,7 +2,7 @@ * @name Taint sinks * @description Expressions that are vulnerable if containing untrusted data. * @kind problem - * @problem.severity informational + * @problem.severity info * @id js/summary/taint-sinks * @tags summary * @precision medium diff --git a/javascript/ql/src/Summary/TaintSources.ql b/javascript/ql/src/Summary/TaintSources.ql index 7178a76dde9..78e544f0bd5 100644 --- a/javascript/ql/src/Summary/TaintSources.ql +++ b/javascript/ql/src/Summary/TaintSources.ql @@ -2,7 +2,7 @@ * @name Taint sources * @description Sources of untrusted input. * @kind problem - * @problem.severity informational + * @problem.severity info * @id js/summary/taint-sources * @tags summary * @precision medium From 16b62486e9708a4b2e4395d686335fdb5cba9441 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Sun, 18 Apr 2021 12:17:47 +0200 Subject: [PATCH 042/550] Python: Extract SensitiveDataHeuristics to be shared with JS Initially I had called `nameIndicatesSensitiveData` for `maybeSensitiveName`, which made the relationship with `maybeSensitive` and `notSensitive` quite strange -- and therefore I added the more informative `maybeSensitiveRegexp` and `notSensitiveRegexp`. Although I'm no longer using `maybeSensitiveName`, and I no longer have a strong argument for making this name change, I still like it. If someone thinks this is a terrible idea, I'm happy to change it though :+1: --- config/identical-files.json | 6 +- .../internal/SensitiveDataHeuristics.qll | 128 ++++++++++++++++++ .../semmle/python/security/SensitiveData.qll | 93 ++++--------- .../internal/SensitiveDataHeuristics.qll | 128 ++++++++++++++++++ 4 files changed, 285 insertions(+), 70 deletions(-) create mode 100644 javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll create mode 100644 python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll diff --git a/config/identical-files.json b/config/identical-files.json index 3916e95a0cf..db622682004 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -439,5 +439,9 @@ "CryptoAlgorithms Python/JS": [ "javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll", "python/ql/src/semmle/crypto/Crypto.qll" + ], + "SensitiveDataHeuristics Python/JS": [ + "javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll", + "python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll" ] -} \ No newline at end of file +} diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll new file mode 100644 index 00000000000..f747241c28e --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -0,0 +1,128 @@ +/** + * INTERNAL: Do not use. + * + * Provides classes and predicates for identifying strings that may indicate the presence of sensitive data. + * Such that we can share this logic across our CodeQL analysis of different languages. + * + * 'Sensitive' data in general is anything that should not be sent around in unencrypted form. + */ + +/** + * A classification of different kinds of sensitive data: + * + * - secret: generic secret or trusted data; + * - id: a user name or other account information; + * - password: a password or authorization key; + * - certificate: a certificate. + * + * While classifications are represented as strings, this should not be relied upon. + * Instead, use the predicates in `SensitiveDataClassification::` to work with + * classifications. + */ +class SensitiveDataClassification extends string { + SensitiveDataClassification() { this in ["secret", "id", "password", "certificate"] } +} + +/** + * Provides predicates to select the different kinds of sensitive data we support. + */ +module SensitiveDataClassification { + /** Gets the classification for secret or trusted data. */ + SensitiveDataClassification secret() { result = "secret" } + + /** Gets the classification for user names or other account information. */ + SensitiveDataClassification id() { result = "id" } + + /** Gets the classification for passwords or authorization keys. */ + SensitiveDataClassification password() { result = "password" } + + /** Gets the classification for certificates. */ + SensitiveDataClassification certificate() { result = "certificate" } +} + +/** + * INTERNAL: Do not use. + * + * Provides heuristics for identifying names related to sensitive information. + */ +module HeuristicNames { + /** + * Gets a regular expression that identifies strings that may indicate the presence of secret + * or trusted data. + */ + string maybeSecret() { result = "(?is).*((? Date: Wed, 21 Apr 2021 10:44:28 +0200 Subject: [PATCH 043/550] Python: Update SensitiveDataHeuristics with newer JS version which also prompted me to rewrite the QLDoc for `nameIndicatesSensitiveData` --- .../security/internal/SensitiveDataHeuristics.qll | 10 ++++++---- .../security/internal/SensitiveDataHeuristics.qll | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index f747241c28e..71f64d98e70 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -93,10 +93,11 @@ module HeuristicNames { /** * Gets a regular expression that identifies strings that may indicate the presence of data - * that is hashed or encrypted, and hence rendered non-sensitive. + * that is hashed or encrypted, and hence rendered non-sensitive, or contains special characters + * suggesting nouns within the string do not represent the meaning of the whole string (e.g. a URL or a SQL query). */ string notSensitiveRegexp() { - result = "(?is).*(redact|censor|obfuscate|hash|md5|sha|((? Date: Wed, 21 Apr 2021 11:19:33 +0200 Subject: [PATCH 044/550] JS: Adapt SensitiveActions to use shared lib Although there are warnings for the new deprecated classes/predicates, the test in javascript/ql/test/library-tests/SensitiveActions/ passes :+1: --- .../javascript/security/SensitiveActions.qll | 97 +++---------------- 1 file changed, 13 insertions(+), 84 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll index 0a5a701ab82..e92229ab714 100644 --- a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll @@ -10,67 +10,7 @@ */ import javascript - -/** - * Provides heuristics for identifying names related to sensitive information. - * - * INTERNAL: Do not use directly. - */ -module HeuristicNames { - /** - * Gets a regular expression that identifies strings that may indicate the presence of secret - * or trusted data. - */ - string maybeSecret() { result = "(?is).*((? Date: Wed, 21 Apr 2021 11:22:48 +0200 Subject: [PATCH 045/550] JS: Rewrite to use SensitiveDataClassification --- .../javascript/security/SensitiveActions.qll | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll index e92229ab714..19bb3f0c6a2 100644 --- a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll @@ -19,7 +19,7 @@ abstract class SensitiveExpr extends Expr { abstract string describe(); /** Gets a classification of the kind of sensitive data this expression might contain. */ - abstract SensitiveExpr::Classification getClassification(); + abstract SensitiveDataClassification getClassification(); } /** DEPRECATED: Use `SensitiveDataClassification` and helpers instead. */ @@ -42,7 +42,7 @@ deprecated module SensitiveExpr { /** A function call that might produce sensitive data. */ class SensitiveCall extends SensitiveExpr, InvokeExpr { - SensitiveExpr::Classification classification; + SensitiveDataClassification classification; SensitiveCall() { classification = this.getCalleeName().(SensitiveDataFunctionName).getClassification() @@ -57,7 +57,7 @@ class SensitiveCall extends SensitiveExpr, InvokeExpr { override string describe() { result = "a call to " + getCalleeName() } - override SensitiveExpr::Classification getClassification() { result = classification } + override SensitiveDataClassification getClassification() { result = classification } } /** An access to a variable or property that might contain sensitive data. */ @@ -81,7 +81,7 @@ abstract class SensitiveWrite extends DataFlow::Node { } /** A write to a variable or property that might contain sensitive data. */ private class BasicSensitiveWrite extends SensitiveWrite { - SensitiveExpr::Classification classification; + SensitiveDataClassification classification; BasicSensitiveWrite() { exists(string name | @@ -102,18 +102,18 @@ private class BasicSensitiveWrite extends SensitiveWrite { } /** Gets a classification of the kind of sensitive data the write might handle. */ - SensitiveExpr::Classification getClassification() { result = classification } + SensitiveDataClassification getClassification() { result = classification } } /** An access to a variable or property that might contain sensitive data. */ private class BasicSensitiveVariableAccess extends SensitiveVariableAccess { - SensitiveExpr::Classification classification; + SensitiveDataClassification classification; BasicSensitiveVariableAccess() { name.regexpMatch(maybeSensitive(classification)) and not name.regexpMatch(notSensitive()) } - override SensitiveExpr::Classification getClassification() { result = classification } + override SensitiveDataClassification getClassification() { result = classification } } /** A function name that suggests it may be sensitive. */ @@ -128,16 +128,16 @@ abstract class SensitiveFunctionName extends string { /** A function name that suggests it may produce sensitive data. */ abstract class SensitiveDataFunctionName extends SensitiveFunctionName { /** Gets a classification of the kind of sensitive data this function may produce. */ - abstract SensitiveExpr::Classification getClassification(); + abstract SensitiveDataClassification getClassification(); } /** A method that might return sensitive data, based on the name. */ class CredentialsFunctionName extends SensitiveDataFunctionName { - SensitiveExpr::Classification classification; + SensitiveDataClassification classification; CredentialsFunctionName() { this.regexpMatch(maybeSensitive(classification)) } - override SensitiveExpr::Classification getClassification() { result = classification } + override SensitiveDataClassification getClassification() { result = classification } } /** @@ -173,7 +173,7 @@ class CleartextPasswordExpr extends SensitiveExpr { override string describe() { none() } - override SensitiveExpr::Classification getClassification() { none() } + override SensitiveDataClassification getClassification() { none() } } /** From b6f8e5057b810988348d0bda9f4c10ef2c1fcc3f Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 21 Apr 2021 11:29:46 +0200 Subject: [PATCH 046/550] JS: Rewrite to use SensitiveDataClassification::password (and like) --- .../ql/src/semmle/javascript/security/SensitiveActions.qll | 4 +++- .../security/dataflow/CleartextStorageCustomizations.qll | 2 +- .../dataflow/InsufficientPasswordHashCustomizations.qll | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll index 19bb3f0c6a2..86b552b7a59 100644 --- a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll @@ -169,7 +169,9 @@ class ProtectCall extends DataFlow::CallNode { /** An expression that might contain a clear-text password. */ class CleartextPasswordExpr extends SensitiveExpr { - CleartextPasswordExpr() { this.(SensitiveExpr).getClassification() = SensitiveExpr::password() } + CleartextPasswordExpr() { + this.(SensitiveExpr).getClassification() = SensitiveDataClassification::password() + } override string describe() { none() } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll index e365b6e74b4..e719d02d100 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll @@ -35,7 +35,7 @@ module CleartextStorage { SensitiveExprSource() { // storing user names or account names in plaintext isn't usually a problem - astNode.getClassification() != SensitiveExpr::id() + astNode.getClassification() != SensitiveDataClassification::id() } override string describe() { result = astNode.describe() } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsufficientPasswordHashCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsufficientPasswordHashCustomizations.qll index d1824d25bd2..82ffe21e131 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsufficientPasswordHashCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsufficientPasswordHashCustomizations.qll @@ -34,7 +34,9 @@ module InsufficientPasswordHash { class CleartextPasswordSource extends Source, DataFlow::ValueNode { override SensitiveExpr astNode; - CleartextPasswordSource() { astNode.getClassification() = SensitiveExpr::password() } + CleartextPasswordSource() { + astNode.getClassification() = SensitiveDataClassification::password() + } override string describe() { result = astNode.describe() } } From b9a1a1fd5cbb357d9db36d131498e9d7102f0f35 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 21 Apr 2021 11:26:17 +0200 Subject: [PATCH 047/550] JS: Rewrite to use nameIndicatesSensitiveData I added this predicate mostly because it was nice with an easy shortcut for it, but also since I spotted the `CredentialsFunctionName` not checking agaisnt the regexps in `notSensitive`, which looked suspicious. So the main goal of adding `nameIndicatesSensitiveData` is that you don't accidentially forget to ensure that the name doesn't match against `notSensitve`. --- .../javascript/security/SensitiveActions.qll | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll index 86b552b7a59..5c96bf3fd7d 100644 --- a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll @@ -50,8 +50,7 @@ class SensitiveCall extends SensitiveExpr, InvokeExpr { // This is particularly to pick up methods with an argument like "password", which // may indicate a lookup. exists(string s | this.getAnArgument().mayHaveStringValue(s) | - s.regexpMatch(maybeSensitive(classification)) and - not s.regexpMatch(notSensitive()) + nameIndicatesSensitiveData(s, classification) ) } @@ -84,10 +83,7 @@ private class BasicSensitiveWrite extends SensitiveWrite { SensitiveDataClassification classification; BasicSensitiveWrite() { - exists(string name | - name.regexpMatch(maybeSensitive(classification)) and - not name.regexpMatch(notSensitive()) - | + exists(string name | nameIndicatesSensitiveData(name, classification) | exists(DataFlow::PropWrite pwn | pwn.getPropertyName() = name and pwn.getRhs() = this @@ -109,9 +105,7 @@ private class BasicSensitiveWrite extends SensitiveWrite { private class BasicSensitiveVariableAccess extends SensitiveVariableAccess { SensitiveDataClassification classification; - BasicSensitiveVariableAccess() { - name.regexpMatch(maybeSensitive(classification)) and not name.regexpMatch(notSensitive()) - } + BasicSensitiveVariableAccess() { nameIndicatesSensitiveData(name, classification) } override SensitiveDataClassification getClassification() { result = classification } } @@ -135,7 +129,11 @@ abstract class SensitiveDataFunctionName extends SensitiveFunctionName { class CredentialsFunctionName extends SensitiveDataFunctionName { SensitiveDataClassification classification; - CredentialsFunctionName() { this.regexpMatch(maybeSensitive(classification)) } + CredentialsFunctionName() { + // TODO: is it by purpose that we don't check whether `this` does not + // match the regexps in `notSensitive`? + this.regexpMatch(maybeSensitive(classification)) + } override SensitiveDataClassification getClassification() { result = classification } } From e977d6eb7549e980983adb09398b2798939776b5 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 21 Apr 2021 11:31:03 +0200 Subject: [PATCH 048/550] JS: Rewrite to use notSensitiveRegexp --- .../security/dataflow/CleartextLoggingCustomizations.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll index b0b9632a63c..7be46b4ee98 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll @@ -54,7 +54,7 @@ module CleartextLogging { */ private class NameGuidedNonCleartextPassword extends NonCleartextPassword { NameGuidedNonCleartextPassword() { - exists(string name | name.regexpMatch(notSensitive()) | + exists(string name | name.regexpMatch(notSensitiveRegexp()) | this.asExpr().(VarAccess).getName() = name or this.(DataFlow::PropRead).getPropertyName() = name @@ -94,7 +94,7 @@ module CleartextLogging { * A call that might obfuscate a password, for example through hashing. */ private class ObfuscatorCall extends Barrier, DataFlow::InvokeNode { - ObfuscatorCall() { getCalleeName().regexpMatch(notSensitive()) } + ObfuscatorCall() { getCalleeName().regexpMatch(notSensitiveRegexp()) } } /** @@ -113,7 +113,7 @@ module CleartextLogging { ObjectPasswordPropertySource() { exists(DataFlow::PropWrite write | name.regexpMatch(maybePassword()) and - not name.regexpMatch(notSensitive()) and + not name.regexpMatch(notSensitiveRegexp()) and write = this.(DataFlow::SourceNode).getAPropertyWrite(name) and // avoid safe values assigned to presumably unsafe names not write.getRhs() instanceof NonCleartextPassword From 08e86fdfe59ccf771a862afd00e1c56875ebf083 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 21 Apr 2021 11:38:52 +0200 Subject: [PATCH 049/550] JS: Make CredentialsFunctionName use nameIndicatesSensitiveData Someone from JS team needs to verify that this is actually OK. --- .../ql/src/semmle/javascript/security/SensitiveActions.qll | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll index 5c96bf3fd7d..d66fe65b747 100644 --- a/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/src/semmle/javascript/security/SensitiveActions.qll @@ -129,11 +129,7 @@ abstract class SensitiveDataFunctionName extends SensitiveFunctionName { class CredentialsFunctionName extends SensitiveDataFunctionName { SensitiveDataClassification classification; - CredentialsFunctionName() { - // TODO: is it by purpose that we don't check whether `this` does not - // match the regexps in `notSensitive`? - this.regexpMatch(maybeSensitive(classification)) - } + CredentialsFunctionName() { nameIndicatesSensitiveData(this, classification) } override SensitiveDataClassification getClassification() { result = classification } } From f611d06ed06a1a326de70383d7b2f917baca0a76 Mon Sep 17 00:00:00 2001 From: asgerf Date: Wed, 21 Apr 2021 10:53:10 +0100 Subject: [PATCH 050/550] JS: Add getALocalUse to cheat sheet --- .../data-flow-cheat-sheet-for-javascript.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 1b2e5ca450b..205db1d2032 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -59,6 +59,7 @@ Classes and member predicates in the ``DataFlow::`` module: - `getStringValue `__ -- value of this node if it's is a string constant - `mayHaveBooleanValue `__ -- check if the value is ``true`` or ``false`` - `SourceNode `__ extends `Node `__ -- function call, parameter, object creation, or reference to a property or global variable + - `getALocalUse `__ -- find nodes whose value came from this node - `getACall `__ -- find calls with this as the callee - `getAnInstantiation `__ -- find ``new``-calls with this as the callee - `getAnInvocation `__ -- find calls or ``new``-calls with this as the callee From 13655b5d80cda15a22eb49941be2a3ddc50e6969 Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Wed, 21 Apr 2021 13:08:35 +0300 Subject: [PATCH 051/550] Add RegExUtils --- .../Security/CWE/CWE-730/RegexInjection.ql | 29 +++++- .../security/CWE-730/RegexInjection.expected | 96 ++++++++++++------- .../security/CWE-730/RegexInjection.java | 54 ++++++++++- .../query-tests/security/CWE-730/options | 2 +- 4 files changed, 138 insertions(+), 43 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql index 58d0d81d615..9a96805c7e5 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -21,22 +21,39 @@ class RegexSink extends DataFlow::ExprNode { RegexSink() { exists(MethodAccess ma, Method m | m = ma.getMethod() | ( - ma.getArgument(0) = this.asExpr() and + m.getDeclaringType().hasQualifiedName("java.lang", "String") and ( - m.getDeclaringType().hasQualifiedName("java.lang", "String") and + ma.getArgument(0) = this.asExpr() and ( m.hasName("matches") or m.hasName("split") or m.hasName("replaceFirst") or m.hasName("replaceAll") ) - or - m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and + ) + or + m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and + ( + ma.getArgument(0) = this.asExpr() and ( m.hasName("compile") or m.hasName("matches") ) ) + or + m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and + ( + ma.getArgument(1) = this.asExpr() and + m.getParameterType(1).(Class).hasQualifiedName("java.lang", "String") and + ( + m.hasName("removeAll") or + m.hasName("removeFirst") or + m.hasName("removePattern") or + m.hasName("replaceAll") or + m.hasName("replaceFirst") or + m.hasName("replacePattern") + ) + ) ) ) } @@ -51,7 +68,9 @@ class RegExpSanitizationCall extends Sanitizer { sanitize = "(?:escape|saniti[sz]e)" and regexp = "regexp?" | - calleeName.regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + ".*)") + calleeName + .regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + + ".*)") ) } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected index 521a45d3d47..93961372d75 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected @@ -1,39 +1,63 @@ edges -| RegexInjection.java:11:22:11:52 | getParameter(...) : String | RegexInjection.java:14:26:14:47 | ... + ... | -| RegexInjection.java:18:22:18:52 | getParameter(...) : String | RegexInjection.java:21:24:21:30 | pattern | -| RegexInjection.java:25:22:25:52 | getParameter(...) : String | RegexInjection.java:28:31:28:37 | pattern | -| RegexInjection.java:32:22:32:52 | getParameter(...) : String | RegexInjection.java:35:29:35:35 | pattern | -| RegexInjection.java:39:22:39:52 | getParameter(...) : String | RegexInjection.java:42:34:42:40 | pattern | -| RegexInjection.java:49:22:49:52 | getParameter(...) : String | RegexInjection.java:52:28:52:34 | pattern | -| RegexInjection.java:56:22:56:52 | getParameter(...) : String | RegexInjection.java:59:28:59:34 | pattern | -| RegexInjection.java:63:22:63:52 | getParameter(...) : String | RegexInjection.java:66:36:66:42 | pattern : String | -| RegexInjection.java:66:32:66:43 | foo(...) : String | RegexInjection.java:66:26:66:52 | ... + ... | -| RegexInjection.java:66:36:66:42 | pattern : String | RegexInjection.java:66:32:66:43 | foo(...) : String | +| RegexInjection.java:13:22:13:52 | getParameter(...) : String | RegexInjection.java:16:26:16:47 | ... + ... | +| RegexInjection.java:20:22:20:52 | getParameter(...) : String | RegexInjection.java:23:24:23:30 | pattern | +| RegexInjection.java:27:22:27:52 | getParameter(...) : String | RegexInjection.java:30:31:30:37 | pattern | +| RegexInjection.java:34:22:34:52 | getParameter(...) : String | RegexInjection.java:37:29:37:35 | pattern | +| RegexInjection.java:41:22:41:52 | getParameter(...) : String | RegexInjection.java:44:34:44:40 | pattern | +| RegexInjection.java:51:22:51:52 | getParameter(...) : String | RegexInjection.java:54:28:54:34 | pattern | +| RegexInjection.java:58:22:58:52 | getParameter(...) : String | RegexInjection.java:61:28:61:34 | pattern | +| RegexInjection.java:65:22:65:52 | getParameter(...) : String | RegexInjection.java:68:36:68:42 | pattern : String | +| RegexInjection.java:68:32:68:43 | foo(...) : String | RegexInjection.java:68:26:68:52 | ... + ... | +| RegexInjection.java:68:36:68:42 | pattern : String | RegexInjection.java:68:32:68:43 | foo(...) : String | +| RegexInjection.java:90:22:90:52 | getParameter(...) : String | RegexInjection.java:93:40:93:46 | pattern | +| RegexInjection.java:97:22:97:52 | getParameter(...) : String | RegexInjection.java:100:42:100:48 | pattern | +| RegexInjection.java:104:22:104:52 | getParameter(...) : String | RegexInjection.java:107:44:107:50 | pattern | +| RegexInjection.java:111:22:111:52 | getParameter(...) : String | RegexInjection.java:114:41:114:47 | pattern | +| RegexInjection.java:118:22:118:52 | getParameter(...) : String | RegexInjection.java:121:43:121:49 | pattern | +| RegexInjection.java:133:22:133:52 | getParameter(...) : String | RegexInjection.java:136:45:136:51 | pattern | nodes -| RegexInjection.java:11:22:11:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:14:26:14:47 | ... + ... | semmle.label | ... + ... | -| RegexInjection.java:18:22:18:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:21:24:21:30 | pattern | semmle.label | pattern | -| RegexInjection.java:25:22:25:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:28:31:28:37 | pattern | semmle.label | pattern | -| RegexInjection.java:32:22:32:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:35:29:35:35 | pattern | semmle.label | pattern | -| RegexInjection.java:39:22:39:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:42:34:42:40 | pattern | semmle.label | pattern | -| RegexInjection.java:49:22:49:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:52:28:52:34 | pattern | semmle.label | pattern | -| RegexInjection.java:56:22:56:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:59:28:59:34 | pattern | semmle.label | pattern | -| RegexInjection.java:63:22:63:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:66:26:66:52 | ... + ... | semmle.label | ... + ... | -| RegexInjection.java:66:32:66:43 | foo(...) : String | semmle.label | foo(...) : String | -| RegexInjection.java:66:36:66:42 | pattern : String | semmle.label | pattern : String | +| RegexInjection.java:13:22:13:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:16:26:16:47 | ... + ... | semmle.label | ... + ... | +| RegexInjection.java:20:22:20:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:23:24:23:30 | pattern | semmle.label | pattern | +| RegexInjection.java:27:22:27:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:30:31:30:37 | pattern | semmle.label | pattern | +| RegexInjection.java:34:22:34:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:37:29:37:35 | pattern | semmle.label | pattern | +| RegexInjection.java:41:22:41:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:44:34:44:40 | pattern | semmle.label | pattern | +| RegexInjection.java:51:22:51:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:54:28:54:34 | pattern | semmle.label | pattern | +| RegexInjection.java:58:22:58:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:61:28:61:34 | pattern | semmle.label | pattern | +| RegexInjection.java:65:22:65:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:68:26:68:52 | ... + ... | semmle.label | ... + ... | +| RegexInjection.java:68:32:68:43 | foo(...) : String | semmle.label | foo(...) : String | +| RegexInjection.java:68:36:68:42 | pattern : String | semmle.label | pattern : String | +| RegexInjection.java:90:22:90:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:93:40:93:46 | pattern | semmle.label | pattern | +| RegexInjection.java:97:22:97:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:100:42:100:48 | pattern | semmle.label | pattern | +| RegexInjection.java:104:22:104:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:107:44:107:50 | pattern | semmle.label | pattern | +| RegexInjection.java:111:22:111:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:114:41:114:47 | pattern | semmle.label | pattern | +| RegexInjection.java:118:22:118:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:121:43:121:49 | pattern | semmle.label | pattern | +| RegexInjection.java:133:22:133:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:136:45:136:51 | pattern | semmle.label | pattern | #select -| RegexInjection.java:14:26:14:47 | ... + ... | RegexInjection.java:11:22:11:52 | getParameter(...) : String | RegexInjection.java:14:26:14:47 | ... + ... | $@ is user controlled. | RegexInjection.java:11:22:11:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:21:24:21:30 | pattern | RegexInjection.java:18:22:18:52 | getParameter(...) : String | RegexInjection.java:21:24:21:30 | pattern | $@ is user controlled. | RegexInjection.java:18:22:18:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:28:31:28:37 | pattern | RegexInjection.java:25:22:25:52 | getParameter(...) : String | RegexInjection.java:28:31:28:37 | pattern | $@ is user controlled. | RegexInjection.java:25:22:25:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:35:29:35:35 | pattern | RegexInjection.java:32:22:32:52 | getParameter(...) : String | RegexInjection.java:35:29:35:35 | pattern | $@ is user controlled. | RegexInjection.java:32:22:32:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:42:34:42:40 | pattern | RegexInjection.java:39:22:39:52 | getParameter(...) : String | RegexInjection.java:42:34:42:40 | pattern | $@ is user controlled. | RegexInjection.java:39:22:39:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:52:28:52:34 | pattern | RegexInjection.java:49:22:49:52 | getParameter(...) : String | RegexInjection.java:52:28:52:34 | pattern | $@ is user controlled. | RegexInjection.java:49:22:49:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:59:28:59:34 | pattern | RegexInjection.java:56:22:56:52 | getParameter(...) : String | RegexInjection.java:59:28:59:34 | pattern | $@ is user controlled. | RegexInjection.java:56:22:56:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:66:26:66:52 | ... + ... | RegexInjection.java:63:22:63:52 | getParameter(...) : String | RegexInjection.java:66:26:66:52 | ... + ... | $@ is user controlled. | RegexInjection.java:63:22:63:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:16:26:16:47 | ... + ... | RegexInjection.java:13:22:13:52 | getParameter(...) : String | RegexInjection.java:16:26:16:47 | ... + ... | $@ is user controlled. | RegexInjection.java:13:22:13:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:23:24:23:30 | pattern | RegexInjection.java:20:22:20:52 | getParameter(...) : String | RegexInjection.java:23:24:23:30 | pattern | $@ is user controlled. | RegexInjection.java:20:22:20:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:30:31:30:37 | pattern | RegexInjection.java:27:22:27:52 | getParameter(...) : String | RegexInjection.java:30:31:30:37 | pattern | $@ is user controlled. | RegexInjection.java:27:22:27:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:37:29:37:35 | pattern | RegexInjection.java:34:22:34:52 | getParameter(...) : String | RegexInjection.java:37:29:37:35 | pattern | $@ is user controlled. | RegexInjection.java:34:22:34:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:44:34:44:40 | pattern | RegexInjection.java:41:22:41:52 | getParameter(...) : String | RegexInjection.java:44:34:44:40 | pattern | $@ is user controlled. | RegexInjection.java:41:22:41:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:54:28:54:34 | pattern | RegexInjection.java:51:22:51:52 | getParameter(...) : String | RegexInjection.java:54:28:54:34 | pattern | $@ is user controlled. | RegexInjection.java:51:22:51:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:61:28:61:34 | pattern | RegexInjection.java:58:22:58:52 | getParameter(...) : String | RegexInjection.java:61:28:61:34 | pattern | $@ is user controlled. | RegexInjection.java:58:22:58:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:68:26:68:52 | ... + ... | RegexInjection.java:65:22:65:52 | getParameter(...) : String | RegexInjection.java:68:26:68:52 | ... + ... | $@ is user controlled. | RegexInjection.java:65:22:65:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:93:40:93:46 | pattern | RegexInjection.java:90:22:90:52 | getParameter(...) : String | RegexInjection.java:93:40:93:46 | pattern | $@ is user controlled. | RegexInjection.java:90:22:90:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:100:42:100:48 | pattern | RegexInjection.java:97:22:97:52 | getParameter(...) : String | RegexInjection.java:100:42:100:48 | pattern | $@ is user controlled. | RegexInjection.java:97:22:97:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:107:44:107:50 | pattern | RegexInjection.java:104:22:104:52 | getParameter(...) : String | RegexInjection.java:107:44:107:50 | pattern | $@ is user controlled. | RegexInjection.java:104:22:104:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:114:41:114:47 | pattern | RegexInjection.java:111:22:111:52 | getParameter(...) : String | RegexInjection.java:114:41:114:47 | pattern | $@ is user controlled. | RegexInjection.java:111:22:111:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:121:43:121:49 | pattern | RegexInjection.java:118:22:118:52 | getParameter(...) : String | RegexInjection.java:121:43:121:49 | pattern | $@ is user controlled. | RegexInjection.java:118:22:118:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:136:45:136:51 | pattern | RegexInjection.java:133:22:133:52 | getParameter(...) : String | RegexInjection.java:136:45:136:51 | pattern | $@ is user controlled. | RegexInjection.java:133:22:133:52 | getParameter(...) | This regular expression pattern | diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java index c203360e105..43218202acf 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java @@ -6,6 +6,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; +import org.apache.commons.lang3.RegExUtils; + public class RegexInjection extends HttpServlet { public boolean string1(javax.servlet.http.HttpServletRequest request) { String pattern = request.getParameter("pattern"); @@ -83,4 +85,54 @@ public class RegexInjection extends HttpServlet { String escapeSpecialRegexChars(String str) { return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0"); } -} \ No newline at end of file + + public boolean apache1(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.removeAll(input, pattern).length() > 0; // BAD + } + + public boolean apache2(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.removeFirst(input, pattern).length() > 0; // BAD + } + + public boolean apache3(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.removePattern(input, pattern).length() > 0; // BAD + } + + public boolean apache4(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.replaceAll(input, pattern, "").length() > 0; // BAD + } + + public boolean apache5(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.replaceFirst(input, pattern, "").length() > 0; // BAD + } + + public boolean apache6(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + Pattern pt = (Pattern)(Object) pattern; + return RegExUtils.replaceFirst(input, pt, "").length() > 0; // GOOD, Pattern compile is the sink instead + } + + public boolean apache7(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + return RegExUtils.replacePattern(input, pattern, "").length() > 0; // BAD + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/options b/java/ql/test/experimental/query-tests/security/CWE-730/options index dd125ad0f4e..73f1b5a3c3e 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-730/options +++ b/java/ql/test/experimental/query-tests/security/CWE-730/options @@ -1 +1 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4 \ No newline at end of file +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/apache-commons-lang3-3.7 \ No newline at end of file From 452ec8c43f005b4ee7f84cbb6b702ce82f037d3b Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Wed, 21 Apr 2021 13:12:53 +0300 Subject: [PATCH 052/550] comments --- .../Security/CWE/CWE-730/RegexInjection.ql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql index 9a96805c7e5..451e8fe816b 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -17,6 +17,9 @@ import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking import DataFlow::PathGraph +/** + * A data flow sink for untrusted user input used to construct regular expressions. + */ class RegexSink extends DataFlow::ExprNode { RegexSink() { exists(MethodAccess ma, Method m | m = ma.getMethod() | @@ -61,6 +64,10 @@ class RegexSink extends DataFlow::ExprNode { abstract class Sanitizer extends DataFlow::ExprNode { } +/** + * A call to a function whose name suggests that it escapes regular + * expression meta-characters. + */ class RegExpSanitizationCall extends Sanitizer { RegExpSanitizationCall() { exists(string calleeName, string sanitize, string regexp | @@ -75,6 +82,9 @@ class RegExpSanitizationCall extends Sanitizer { } } +/** + * A taint-tracking configuration for untrusted user input used to construct regular expressions. + */ class RegexInjectionConfiguration extends TaintTracking::Configuration { RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" } From ff73c0b24783d31378b26fd3f496fd4533a6425a Mon Sep 17 00:00:00 2001 From: asgerf Date: Wed, 21 Apr 2021 11:15:18 +0100 Subject: [PATCH 053/550] JS: Add section with access paths to cheat sheet --- .../data-flow-cheat-sheet-for-javascript.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 205db1d2032..a29d0e63ec9 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -165,6 +165,19 @@ String matching - x.\ `regexpMatch `__\ ("(?i).*escape.*") -- holds if x contains "escape" (case insensitive) +Access paths +------------ + +When multiple property accesses are chained together they form what's called an "access path". + +To identify nodes based on access paths, use the following predicates in `AccessPath `__ module: + +- AccessPath::`getAReferenceTo `__ -- find nodes that refer to the given access path +- AccessPath::`getAnAssignmentTo `__ -- finds nodes that are assigned to the given access path +- AccessPath::`getAnAliasedSourceNode `__ -- finds nodes that refer to the same access path + +``getAReferenceTo`` and ``getAnAssignmentTo`` have a 1-argument version for global access paths, and a 2-argument version for access paths starting at a given node. + Type tracking ------------- From 5df85830562144b3c873ef110cd9cffbc31f4d0a Mon Sep 17 00:00:00 2001 From: asgerf Date: Wed, 21 Apr 2021 11:26:06 +0100 Subject: [PATCH 054/550] JS: Mention isUserControlledObject --- .../data-flow-cheat-sheet-for-javascript.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index a29d0e63ec9..7d6ba99c843 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -132,6 +132,7 @@ System and Network - `PersistentReadAccess `__ -- reading from persistent storage, like cookies - `PersistentWriteAccess `__ -- writing to persistent storage - `RemoteFlowSource `__ -- source of untrusted user input + - `isUserControlledObject` -- is the input deserialized to a JSON-like object? (as opposed to just being a string) - `SystemCommandExecution `__ -- execution of a system command Files From 226792c73a6cec31880a5501d0dc111783cd5746 Mon Sep 17 00:00:00 2001 From: asgerf Date: Wed, 21 Apr 2021 11:33:04 +0100 Subject: [PATCH 055/550] JS: Expand RemoteFlowSource and move into own section --- .../data-flow-cheat-sheet-for-javascript.rst | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 7d6ba99c843..79bc28f5c16 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -131,10 +131,23 @@ System and Network - `FileSystemWriteAccess `__ -- writing to the contents of a file - `PersistentReadAccess `__ -- reading from persistent storage, like cookies - `PersistentWriteAccess `__ -- writing to persistent storage -- `RemoteFlowSource `__ -- source of untrusted user input - - `isUserControlledObject` -- is the input deserialized to a JSON-like object? (as opposed to just being a string) - `SystemCommandExecution `__ -- execution of a system command +Untrusted Data +-------------- + +- `RemoteFlowSource `__ -- source of untrusted user input + - `isUserControlledObject `__ -- is the input deserialized to a JSON-like object? (as opposed to just being a string) +- `ClientSideRemoteFlowSource `__ extends `RemoteFlowSource `__ -- input specific to the browser environment + - `getKind `__ -- is this derived from the ``path``, ``fragment``, ``query``, ``url``, or ``name``? +- HTTP::`RequestInputAccess `__ extends `RemoteFlowSource `__ -- input from an incoming HTTP request + - `getKind `__ -- is this derived from a ``parameter``, ``header``, ``body``, ``url``, or ``cookie``? +- HTTP::`RequestHeaderAccess `__ extends `RequestInputAccess `__ -- access to a specific header + - `getAHeaderName `__ -- the name of a header being accessed + +Note: some `RemoteFlowSource `__ instances, such as input from a web socket, +belong to none of the specific subcategories above. + Files ----- From 9774b24c4e297655148654047fa66d1fd133be34 Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Thu, 22 Apr 2021 09:44:07 +0300 Subject: [PATCH 056/550] Use TypeString --- .../src/experimental/Security/CWE/CWE-730/RegexInjection.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql index 451e8fe816b..c33b7ecf3af 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -24,7 +24,7 @@ class RegexSink extends DataFlow::ExprNode { RegexSink() { exists(MethodAccess ma, Method m | m = ma.getMethod() | ( - m.getDeclaringType().hasQualifiedName("java.lang", "String") and + m.getDeclaringType() instanceof TypeString and ( ma.getArgument(0) = this.asExpr() and ( @@ -47,7 +47,7 @@ class RegexSink extends DataFlow::ExprNode { m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and ( ma.getArgument(1) = this.asExpr() and - m.getParameterType(1).(Class).hasQualifiedName("java.lang", "String") and + m.getParameterType(1).(Class) instanceof TypeString and ( m.hasName("removeAll") or m.hasName("removeFirst") or From 86444bfa09e2e48ec37801abf3b80d537dce4f92 Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Thu, 22 Apr 2021 09:48:46 +0300 Subject: [PATCH 057/550] Use set literal expression --- .../Security/CWE/CWE-730/RegexInjection.ql | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql index c33b7ecf3af..ad75e993c30 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -27,35 +27,23 @@ class RegexSink extends DataFlow::ExprNode { m.getDeclaringType() instanceof TypeString and ( ma.getArgument(0) = this.asExpr() and - ( - m.hasName("matches") or - m.hasName("split") or - m.hasName("replaceFirst") or - m.hasName("replaceAll") - ) + m.hasName(["matches", "split", "replaceFirst", "replaceAll"]) ) or m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and ( ma.getArgument(0) = this.asExpr() and - ( - m.hasName("compile") or - m.hasName("matches") - ) + m.hasName(["compile", "matches"]) ) or m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and ( ma.getArgument(1) = this.asExpr() and m.getParameterType(1).(Class) instanceof TypeString and - ( - m.hasName("removeAll") or - m.hasName("removeFirst") or - m.hasName("removePattern") or - m.hasName("replaceAll") or - m.hasName("replaceFirst") or - m.hasName("replacePattern") - ) + m.hasName([ + "removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst", + "replacePattern" + ]) ) ) ) From ade238307f1416ad45af12f2a9c5ab949a04f427 Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Thu, 22 Apr 2021 10:02:06 +0300 Subject: [PATCH 058/550] Add a test --- .../security/CWE-730/RegexInjection.expected | 52 ++++++++++--------- .../security/CWE-730/RegexInjection.java | 10 ++++ 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected index 93961372d75..1dfe806f10b 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.expected @@ -9,12 +9,13 @@ edges | RegexInjection.java:65:22:65:52 | getParameter(...) : String | RegexInjection.java:68:36:68:42 | pattern : String | | RegexInjection.java:68:32:68:43 | foo(...) : String | RegexInjection.java:68:26:68:52 | ... + ... | | RegexInjection.java:68:36:68:42 | pattern : String | RegexInjection.java:68:32:68:43 | foo(...) : String | -| RegexInjection.java:90:22:90:52 | getParameter(...) : String | RegexInjection.java:93:40:93:46 | pattern | -| RegexInjection.java:97:22:97:52 | getParameter(...) : String | RegexInjection.java:100:42:100:48 | pattern | -| RegexInjection.java:104:22:104:52 | getParameter(...) : String | RegexInjection.java:107:44:107:50 | pattern | -| RegexInjection.java:111:22:111:52 | getParameter(...) : String | RegexInjection.java:114:41:114:47 | pattern | -| RegexInjection.java:118:22:118:52 | getParameter(...) : String | RegexInjection.java:121:43:121:49 | pattern | -| RegexInjection.java:133:22:133:52 | getParameter(...) : String | RegexInjection.java:136:45:136:51 | pattern | +| RegexInjection.java:84:22:84:52 | getParameter(...) : String | RegexInjection.java:90:26:90:47 | ... + ... | +| RegexInjection.java:100:22:100:52 | getParameter(...) : String | RegexInjection.java:103:40:103:46 | pattern | +| RegexInjection.java:107:22:107:52 | getParameter(...) : String | RegexInjection.java:110:42:110:48 | pattern | +| RegexInjection.java:114:22:114:52 | getParameter(...) : String | RegexInjection.java:117:44:117:50 | pattern | +| RegexInjection.java:121:22:121:52 | getParameter(...) : String | RegexInjection.java:124:41:124:47 | pattern | +| RegexInjection.java:128:22:128:52 | getParameter(...) : String | RegexInjection.java:131:43:131:49 | pattern | +| RegexInjection.java:143:22:143:52 | getParameter(...) : String | RegexInjection.java:146:45:146:51 | pattern | nodes | RegexInjection.java:13:22:13:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | | RegexInjection.java:16:26:16:47 | ... + ... | semmle.label | ... + ... | @@ -34,18 +35,20 @@ nodes | RegexInjection.java:68:26:68:52 | ... + ... | semmle.label | ... + ... | | RegexInjection.java:68:32:68:43 | foo(...) : String | semmle.label | foo(...) : String | | RegexInjection.java:68:36:68:42 | pattern : String | semmle.label | pattern : String | -| RegexInjection.java:90:22:90:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:93:40:93:46 | pattern | semmle.label | pattern | -| RegexInjection.java:97:22:97:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:100:42:100:48 | pattern | semmle.label | pattern | -| RegexInjection.java:104:22:104:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:107:44:107:50 | pattern | semmle.label | pattern | -| RegexInjection.java:111:22:111:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:114:41:114:47 | pattern | semmle.label | pattern | -| RegexInjection.java:118:22:118:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:121:43:121:49 | pattern | semmle.label | pattern | -| RegexInjection.java:133:22:133:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| RegexInjection.java:136:45:136:51 | pattern | semmle.label | pattern | +| RegexInjection.java:84:22:84:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:90:26:90:47 | ... + ... | semmle.label | ... + ... | +| RegexInjection.java:100:22:100:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:103:40:103:46 | pattern | semmle.label | pattern | +| RegexInjection.java:107:22:107:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:110:42:110:48 | pattern | semmle.label | pattern | +| RegexInjection.java:114:22:114:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:117:44:117:50 | pattern | semmle.label | pattern | +| RegexInjection.java:121:22:121:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:124:41:124:47 | pattern | semmle.label | pattern | +| RegexInjection.java:128:22:128:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:131:43:131:49 | pattern | semmle.label | pattern | +| RegexInjection.java:143:22:143:52 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| RegexInjection.java:146:45:146:51 | pattern | semmle.label | pattern | #select | RegexInjection.java:16:26:16:47 | ... + ... | RegexInjection.java:13:22:13:52 | getParameter(...) : String | RegexInjection.java:16:26:16:47 | ... + ... | $@ is user controlled. | RegexInjection.java:13:22:13:52 | getParameter(...) | This regular expression pattern | | RegexInjection.java:23:24:23:30 | pattern | RegexInjection.java:20:22:20:52 | getParameter(...) : String | RegexInjection.java:23:24:23:30 | pattern | $@ is user controlled. | RegexInjection.java:20:22:20:52 | getParameter(...) | This regular expression pattern | @@ -55,9 +58,10 @@ nodes | RegexInjection.java:54:28:54:34 | pattern | RegexInjection.java:51:22:51:52 | getParameter(...) : String | RegexInjection.java:54:28:54:34 | pattern | $@ is user controlled. | RegexInjection.java:51:22:51:52 | getParameter(...) | This regular expression pattern | | RegexInjection.java:61:28:61:34 | pattern | RegexInjection.java:58:22:58:52 | getParameter(...) : String | RegexInjection.java:61:28:61:34 | pattern | $@ is user controlled. | RegexInjection.java:58:22:58:52 | getParameter(...) | This regular expression pattern | | RegexInjection.java:68:26:68:52 | ... + ... | RegexInjection.java:65:22:65:52 | getParameter(...) : String | RegexInjection.java:68:26:68:52 | ... + ... | $@ is user controlled. | RegexInjection.java:65:22:65:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:93:40:93:46 | pattern | RegexInjection.java:90:22:90:52 | getParameter(...) : String | RegexInjection.java:93:40:93:46 | pattern | $@ is user controlled. | RegexInjection.java:90:22:90:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:100:42:100:48 | pattern | RegexInjection.java:97:22:97:52 | getParameter(...) : String | RegexInjection.java:100:42:100:48 | pattern | $@ is user controlled. | RegexInjection.java:97:22:97:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:107:44:107:50 | pattern | RegexInjection.java:104:22:104:52 | getParameter(...) : String | RegexInjection.java:107:44:107:50 | pattern | $@ is user controlled. | RegexInjection.java:104:22:104:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:114:41:114:47 | pattern | RegexInjection.java:111:22:111:52 | getParameter(...) : String | RegexInjection.java:114:41:114:47 | pattern | $@ is user controlled. | RegexInjection.java:111:22:111:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:121:43:121:49 | pattern | RegexInjection.java:118:22:118:52 | getParameter(...) : String | RegexInjection.java:121:43:121:49 | pattern | $@ is user controlled. | RegexInjection.java:118:22:118:52 | getParameter(...) | This regular expression pattern | -| RegexInjection.java:136:45:136:51 | pattern | RegexInjection.java:133:22:133:52 | getParameter(...) : String | RegexInjection.java:136:45:136:51 | pattern | $@ is user controlled. | RegexInjection.java:133:22:133:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:90:26:90:47 | ... + ... | RegexInjection.java:84:22:84:52 | getParameter(...) : String | RegexInjection.java:90:26:90:47 | ... + ... | $@ is user controlled. | RegexInjection.java:84:22:84:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:103:40:103:46 | pattern | RegexInjection.java:100:22:100:52 | getParameter(...) : String | RegexInjection.java:103:40:103:46 | pattern | $@ is user controlled. | RegexInjection.java:100:22:100:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:110:42:110:48 | pattern | RegexInjection.java:107:22:107:52 | getParameter(...) : String | RegexInjection.java:110:42:110:48 | pattern | $@ is user controlled. | RegexInjection.java:107:22:107:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:117:44:117:50 | pattern | RegexInjection.java:114:22:114:52 | getParameter(...) : String | RegexInjection.java:117:44:117:50 | pattern | $@ is user controlled. | RegexInjection.java:114:22:114:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:124:41:124:47 | pattern | RegexInjection.java:121:22:121:52 | getParameter(...) : String | RegexInjection.java:124:41:124:47 | pattern | $@ is user controlled. | RegexInjection.java:121:22:121:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:131:43:131:49 | pattern | RegexInjection.java:128:22:128:52 | getParameter(...) : String | RegexInjection.java:131:43:131:49 | pattern | $@ is user controlled. | RegexInjection.java:128:22:128:52 | getParameter(...) | This regular expression pattern | +| RegexInjection.java:146:45:146:51 | pattern | RegexInjection.java:143:22:143:52 | getParameter(...) : String | RegexInjection.java:146:45:146:51 | pattern | $@ is user controlled. | RegexInjection.java:143:22:143:52 | getParameter(...) | This regular expression pattern | diff --git a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java index 43218202acf..2aace93aeb8 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java +++ b/java/ql/test/experimental/query-tests/security/CWE-730/RegexInjection.java @@ -80,6 +80,16 @@ public class RegexInjection extends HttpServlet { return input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$"); } + public boolean pattern6(javax.servlet.http.HttpServletRequest request) { + String pattern = request.getParameter("pattern"); + String input = request.getParameter("input"); + + escapeSpecialRegexChars(pattern); + + // BAD: the pattern is not really sanitized + return input.matches("^" + pattern + "=.*$"); + } + Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]"); String escapeSpecialRegexChars(String str) { From 0dceabe704ba363084fecdd58251060b99c8becc Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 22 Apr 2021 09:06:09 +0100 Subject: [PATCH 059/550] JS: Reference specific section of cheat sheet --- .../data-flow-cheat-sheet-for-javascript.rst | 2 ++ ...specifying-additional-remote-flow-sources-for-javascript.rst | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 79bc28f5c16..1b9378669a2 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -133,6 +133,8 @@ System and Network - `PersistentWriteAccess `__ -- writing to persistent storage - `SystemCommandExecution `__ -- execution of a system command +.. _data-flow-cheat-sheet-for-javascript--untrusted-data: + Untrusted Data -------------- diff --git a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst index 082a7e3fcb7..cc3fe5e9469 100644 --- a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst @@ -11,7 +11,7 @@ You can model potential sources of untrusted user input in your code without mak Specifying remote flow sources in external files is currently in beta and subject to change. -As mentioned in the :doc:`Data flow cheat sheet for JavaScript `, the CodeQL libraries for JavaScript +As mentioned in the :ref:`Data flow cheat sheet for JavaScript `, the CodeQL libraries for JavaScript provide a class `RemoteFlowSource `__ to represent sources of untrusted user input, sometimes also referred to as remote flow sources. From d2646ea4ad3be13460a4bc973e9e446e820cc86b Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 22 Apr 2021 09:06:44 +0100 Subject: [PATCH 060/550] JS: More consistent section capitalization --- .../data-flow-cheat-sheet-for-javascript.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 1b9378669a2..7d9ac42c3d8 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -135,7 +135,7 @@ System and Network .. _data-flow-cheat-sheet-for-javascript--untrusted-data: -Untrusted Data +Untrusted data -------------- - `RemoteFlowSource `__ -- source of untrusted user input From cf64701bcb1c33b37b695bc584c5b481d30f2884 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Sat, 27 Feb 2021 10:52:33 +0100 Subject: [PATCH 061/550] Python: Move weak-crypto-algorithm tests to own folder --- .../BrokenCryptoAlgorithm.expected | 0 .../BrokenCryptoAlgorithm.qlref | 0 .../{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/TestNode.expected | 0 .../{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/TestNode.ql | 0 .../query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options | 1 + .../test_cryptography.py | 0 .../{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/test_pycrypto.py | 0 7 files changed, 1 insertion(+) rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/BrokenCryptoAlgorithm.expected (100%) rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/BrokenCryptoAlgorithm.qlref (100%) rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/TestNode.expected (100%) rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/TestNode.ql (100%) create mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/test_cryptography.py (100%) rename python/ql/test/query-tests/Security/{CWE-327 => CWE-327-BrokenCryptoAlgorithm}/test_pycrypto.py (100%) diff --git a/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected diff --git a/python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.qlref b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.qlref similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.qlref rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.qlref diff --git a/python/ql/test/query-tests/Security/CWE-327/TestNode.expected b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/TestNode.expected rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected diff --git a/python/ql/test/query-tests/Security/CWE-327/TestNode.ql b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/TestNode.ql rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options new file mode 100644 index 00000000000..492768b3481 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options @@ -0,0 +1 @@ +semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-327/test_cryptography.py b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/test_cryptography.py rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py diff --git a/python/ql/test/query-tests/Security/CWE-327/test_pycrypto.py b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py similarity index 100% rename from python/ql/test/query-tests/Security/CWE-327/test_pycrypto.py rename to python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py From d18fbb7f07ee18a2cb46d91db0d62cca0fd2e207 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Sat, 27 Feb 2021 17:55:58 +0100 Subject: [PATCH 062/550] Python: Add working tests of AES and RC4 --- .../frameworks/cryptodome/test_aes.py | 36 +++++++++++++++++ .../frameworks/cryptodome/test_rc4.py | 30 ++++++++++++++ .../frameworks/cryptography/test_aes.py | 40 +++++++++++++++++++ .../frameworks/cryptography/test_rc4.py | 32 +++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py new file mode 100644 index 00000000000..971fdadd327 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py @@ -0,0 +1,36 @@ +# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html +from Cryptodome.Cipher import AES + +import os + +key = os.urandom(256//8) +iv = os.urandom(16) + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + + +print("encrypt/decrypt") + +secret_message = b"secret message" + +padding_len = 16 - (len(secret_message) % 16) +padding = b"\0"*padding_len + +cipher = AES.new(key, AES.MODE_CBC, iv=iv) +# using separate .encrypt calls on individual lines does not work +whole_plantext = secret_message + padding +encrypted = cipher.encrypt(whole_plantext) + +print("encrypted={}".format(encrypted)) + +print() + +cipher = AES.new(key, AES.MODE_CBC, iv=iv) +decrypted = cipher.decrypt(encrypted) + +decrypted = decrypted[:-padding_len] + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py new file mode 100644 index 00000000000..a336dca6a49 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py @@ -0,0 +1,30 @@ +# https://pycryptodome.readthedocs.io/en/latest/src/cipher/arc4.html +from Cryptodome.Cipher import ARC4 + +import os + +key = os.urandom(256//8) + + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + + + +print("encrypt/decrypt") + +secret_message = b"secret message" + +cipher = ARC4.new(key) +encrypted = cipher.encrypt(secret_message) + +print("encrypted={}".format(encrypted)) + +print() + +cipher = ARC4.new(key) +decrypted = cipher.decrypt(encrypted) + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py new file mode 100644 index 00000000000..7bd7040d0b4 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py @@ -0,0 +1,40 @@ +from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes +import os + +key = os.urandom(256//8) +iv = os.urandom(16) + +algorithm = algorithms.AES(key) +cipher = Cipher(algorithm, mode=modes.CBC(iv)) + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + +# following https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.Cipher + +print("encrypt/decrypt") + +secret_message = b"secret message" + +padding_len = 16 - (len(secret_message) % 16) +padding = b"\0"*padding_len + +encryptor = cipher.encryptor() +print(padding_len) +encrypted = encryptor.update(secret_message) +encrypted += encryptor.update(padding) +encrypted += encryptor.finalize() + +print("encrypted={}".format(encrypted)) + +print() + +decryptor = cipher.decryptor() +decrypted = decryptor.update(encrypted) +decrypted += decryptor.finalize() + +decrypted = decrypted[:-padding_len] + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py new file mode 100644 index 00000000000..0d3fbcc320c --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py @@ -0,0 +1,32 @@ +from cryptography.hazmat.primitives.ciphers import algorithms, Cipher +import os + +key = os.urandom(256//8) + +algorithm = algorithms.ARC4(key) +cipher = Cipher(algorithm, mode=None) + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + +# following https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption.html#cryptography.hazmat.primitives.ciphers.algorithms.ARC4 + +print("encrypt/decrypt") + +secret_message = b"secret message" + +encryptor = cipher.encryptor() +encrypted = encryptor.update(secret_message) +encrypted += encryptor.finalize() + +print("encrypted={}".format(encrypted)) + +print() + +decryptor = cipher.decryptor() +decrypted = decryptor.update(encrypted) +decrypted += decryptor.finalize() + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message From 65c8d9605e94e6c09b476d749e8cdd62af1c6e15 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Sat, 27 Feb 2021 18:29:53 +0100 Subject: [PATCH 063/550] Python: Add CryptographicOperation Concept I considered using `getInput` like in JS, but things like signature verification has multiple inputs (message and signature). Using getAnInput also aligns better with Decoding/Encoding. --- python/ql/src/semmle/python/Concepts.qll | 48 ++++++++++++++++++- .../test/experimental/meta/ConceptsTest.qll | 30 ++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 4be00ba5bf9..50024f108c0 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -527,7 +527,14 @@ module HTTP { } } -/** Provides models for cryptographic things. */ +/** + * Provides models for cryptographic things. + * + * Note: The `CryptographicAlgorithm` class currently doesn't take weak keys into + * consideration for the `isWeak` member predicate. So RSA is always considered + * secure, although using a low number of bits will actually make it insecure. We plan + * to improve our libraries in the future to more precisely capture this aspect. + */ module Cryptography { /** Provides models for public-key cryptography, also called asymmetric cryptography. */ module PublicKey { @@ -626,4 +633,43 @@ module Cryptography { } } } + + import semmle.crypto.Crypto + + /** + * A data-flow node that is an application of a cryptographic algorithm. For example, + * encryption, decryption, signature-validation. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `CryptographicOperation::Range` instead. + */ + class CryptographicOperation extends DataFlow::Node { + CryptographicOperation::Range range; + + CryptographicOperation() { this = range } + + /** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */ + CryptographicAlgorithm getAlgorithm() { result = range.getAlgorithm() } + + /** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */ + DataFlow::Node getAnInput() { result = range.getAnInput() } + } + + /** Provides classes for modeling new applications of a cryptographic algorithms. */ + module CryptographicOperation { + /** + * A data-flow node that is an application of a cryptographic algorithm. For example, + * encryption, decryption, signature-validation. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `CryptographicOperation` instead. + */ + abstract class Range extends DataFlow::Node { + /** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */ + abstract CryptographicAlgorithm getAlgorithm(); + + /** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */ + abstract DataFlow::Node getAnInput(); + } + } } diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 3800a4cd273..331ccd72c0a 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -341,3 +341,33 @@ class PublicKeyGenerationTest extends InlineExpectationsTest { ) } } + +class CryptographicOperationTest extends InlineExpectationsTest { + CryptographicOperationTest() { this = "CryptographicOperationTest" } + + override string getARelevantTag() { + result in [ + "CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm" + ] + } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(Cryptography::CryptographicOperation cryptoOperation | + location = cryptoOperation.getLocation() and + ( + element = cryptoOperation.toString() and + value = "" and + tag = "CryptographicOperation" + or + element = cryptoOperation.toString() and + value = value_from_expr(cryptoOperation.getAnInput().asExpr()) and + tag = "CryptographicOperationInput" + or + element = cryptoOperation.toString() and + value = cryptoOperation.getAlgorithm().getName() and + tag = "CryptographicOperationAlgorithm" + ) + ) + } +} From a8de2aba3bb0d728eefb9b266f201319e957dfd5 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 15 Apr 2021 13:33:04 +0200 Subject: [PATCH 064/550] Python: Move CryptoAlgorithms implementation --- config/identical-files.json | 4 +- python/ql/src/semmle/crypto/Crypto.qll | 175 +----------------- python/ql/src/semmle/python/Concepts.qll | 2 +- .../python/concepts/CryptoAlgorithms.qll | 174 +++++++++++++++++ 4 files changed, 179 insertions(+), 176 deletions(-) create mode 100644 python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll diff --git a/config/identical-files.json b/config/identical-files.json index 3916e95a0cf..5576404f616 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -438,6 +438,6 @@ ], "CryptoAlgorithms Python/JS": [ "javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll", - "python/ql/src/semmle/crypto/Crypto.qll" + "python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll" ] -} \ No newline at end of file +} diff --git a/python/ql/src/semmle/crypto/Crypto.qll b/python/ql/src/semmle/crypto/Crypto.qll index d9f25b42c9a..5e2bb97a0aa 100644 --- a/python/ql/src/semmle/crypto/Crypto.qll +++ b/python/ql/src/semmle/crypto/Crypto.qll @@ -1,174 +1,3 @@ -/** - * Provides classes modeling cryptographic algorithms, separated into strong and weak variants. - * - * The classification into strong and weak are based on Wikipedia, OWASP and google (2017). - */ +/** DEPRECATED: Use `semmle.python.concepts.CryptoAlgorithms` instead. */ -/** - * Names of cryptographic algorithms, separated into strong and weak variants. - * - * The names are normalized: upper-case, no spaces, dashes or underscores. - * - * The names are inspired by the names used in real world crypto libraries. - * - * The classification into strong and weak are based on Wikipedia, OWASP and google (2017). - */ -private module AlgorithmNames { - predicate isStrongHashingAlgorithm(string name) { - name = "DSA" or - name = "ED25519" or - name = "ES256" or - name = "ECDSA256" or - name = "ES384" or - name = "ECDSA384" or - name = "ES512" or - name = "ECDSA512" or - name = "SHA2" or - name = "SHA224" or - name = "SHA256" or - name = "SHA384" or - name = "SHA512" or - name = "SHA3" - } - - predicate isWeakHashingAlgorithm(string name) { - name = "HAVEL128" or - name = "MD2" or - name = "MD4" or - name = "MD5" or - name = "PANAMA" or - name = "RIPEMD" or - name = "RIPEMD128" or - name = "RIPEMD256" or - name = "RIPEMD160" or - name = "RIPEMD320" or - name = "SHA0" or - name = "SHA1" - } - - predicate isStrongEncryptionAlgorithm(string name) { - name = "AES" or - name = "AES128" or - name = "AES192" or - name = "AES256" or - name = "AES512" or - name = "RSA" or - name = "RABBIT" or - name = "BLOWFISH" - } - - predicate isWeakEncryptionAlgorithm(string name) { - name = "DES" or - name = "3DES" or - name = "TRIPLEDES" or - name = "TDEA" or - name = "TRIPLEDEA" or - name = "ARC2" or - name = "RC2" or - name = "ARC4" or - name = "RC4" or - name = "ARCFOUR" or - name = "ARC5" or - name = "RC5" - } - - predicate isStrongPasswordHashingAlgorithm(string name) { - name = "ARGON2" or - name = "PBKDF2" or - name = "BCRYPT" or - name = "SCRYPT" - } - - predicate isWeakPasswordHashingAlgorithm(string name) { none() } -} - -private import AlgorithmNames - -/** - * A cryptographic algorithm. - */ -private newtype TCryptographicAlgorithm = - MkHashingAlgorithm(string name, boolean isWeak) { - isStrongHashingAlgorithm(name) and isWeak = false - or - isWeakHashingAlgorithm(name) and isWeak = true - } or - MkEncryptionAlgorithm(string name, boolean isWeak) { - isStrongEncryptionAlgorithm(name) and isWeak = false - or - isWeakEncryptionAlgorithm(name) and isWeak = true - } or - MkPasswordHashingAlgorithm(string name, boolean isWeak) { - isStrongPasswordHashingAlgorithm(name) and isWeak = false - or - isWeakPasswordHashingAlgorithm(name) and isWeak = true - } - -/** - * A cryptographic algorithm. - */ -abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { - /** Gets a textual representation of this element. */ - string toString() { result = getName() } - - /** - * Gets the normalized name of this algorithm (upper-case, no spaces, dashes or underscores). - */ - abstract string getName(); - - /** - * Holds if the name of this algorithm matches `name` modulo case, - * white space, dashes, and underscores. - */ - bindingset[name] - predicate matchesName(string name) { - name.toUpperCase().regexpReplaceAll("[-_ ]", "") = getName() - } - - /** - * Holds if this algorithm is weak. - */ - abstract predicate isWeak(); -} - -/** - * A hashing algorithm such as `MD5` or `SHA512`. - */ -class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; - - HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } - - override string getName() { result = name } - - override predicate isWeak() { isWeak = true } -} - -/** - * An encryption algorithm such as `DES` or `AES512`. - */ -class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; - - EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } - - override string getName() { result = name } - - override predicate isWeak() { isWeak = true } -} - -/** - * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. - */ -class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { - string name; - boolean isWeak; - - PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } - - override string getName() { result = name } - - override predicate isWeak() { isWeak = true } -} +import semmle.python.concepts.CryptoAlgorithms diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 50024f108c0..a1edb67dc52 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -634,7 +634,7 @@ module Cryptography { } } - import semmle.crypto.Crypto + import semmle.python.concepts.CryptoAlgorithms /** * A data-flow node that is an application of a cryptographic algorithm. For example, diff --git a/python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll b/python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll new file mode 100644 index 00000000000..d9f25b42c9a --- /dev/null +++ b/python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll @@ -0,0 +1,174 @@ +/** + * Provides classes modeling cryptographic algorithms, separated into strong and weak variants. + * + * The classification into strong and weak are based on Wikipedia, OWASP and google (2017). + */ + +/** + * Names of cryptographic algorithms, separated into strong and weak variants. + * + * The names are normalized: upper-case, no spaces, dashes or underscores. + * + * The names are inspired by the names used in real world crypto libraries. + * + * The classification into strong and weak are based on Wikipedia, OWASP and google (2017). + */ +private module AlgorithmNames { + predicate isStrongHashingAlgorithm(string name) { + name = "DSA" or + name = "ED25519" or + name = "ES256" or + name = "ECDSA256" or + name = "ES384" or + name = "ECDSA384" or + name = "ES512" or + name = "ECDSA512" or + name = "SHA2" or + name = "SHA224" or + name = "SHA256" or + name = "SHA384" or + name = "SHA512" or + name = "SHA3" + } + + predicate isWeakHashingAlgorithm(string name) { + name = "HAVEL128" or + name = "MD2" or + name = "MD4" or + name = "MD5" or + name = "PANAMA" or + name = "RIPEMD" or + name = "RIPEMD128" or + name = "RIPEMD256" or + name = "RIPEMD160" or + name = "RIPEMD320" or + name = "SHA0" or + name = "SHA1" + } + + predicate isStrongEncryptionAlgorithm(string name) { + name = "AES" or + name = "AES128" or + name = "AES192" or + name = "AES256" or + name = "AES512" or + name = "RSA" or + name = "RABBIT" or + name = "BLOWFISH" + } + + predicate isWeakEncryptionAlgorithm(string name) { + name = "DES" or + name = "3DES" or + name = "TRIPLEDES" or + name = "TDEA" or + name = "TRIPLEDEA" or + name = "ARC2" or + name = "RC2" or + name = "ARC4" or + name = "RC4" or + name = "ARCFOUR" or + name = "ARC5" or + name = "RC5" + } + + predicate isStrongPasswordHashingAlgorithm(string name) { + name = "ARGON2" or + name = "PBKDF2" or + name = "BCRYPT" or + name = "SCRYPT" + } + + predicate isWeakPasswordHashingAlgorithm(string name) { none() } +} + +private import AlgorithmNames + +/** + * A cryptographic algorithm. + */ +private newtype TCryptographicAlgorithm = + MkHashingAlgorithm(string name, boolean isWeak) { + isStrongHashingAlgorithm(name) and isWeak = false + or + isWeakHashingAlgorithm(name) and isWeak = true + } or + MkEncryptionAlgorithm(string name, boolean isWeak) { + isStrongEncryptionAlgorithm(name) and isWeak = false + or + isWeakEncryptionAlgorithm(name) and isWeak = true + } or + MkPasswordHashingAlgorithm(string name, boolean isWeak) { + isStrongPasswordHashingAlgorithm(name) and isWeak = false + or + isWeakPasswordHashingAlgorithm(name) and isWeak = true + } + +/** + * A cryptographic algorithm. + */ +abstract class CryptographicAlgorithm extends TCryptographicAlgorithm { + /** Gets a textual representation of this element. */ + string toString() { result = getName() } + + /** + * Gets the normalized name of this algorithm (upper-case, no spaces, dashes or underscores). + */ + abstract string getName(); + + /** + * Holds if the name of this algorithm matches `name` modulo case, + * white space, dashes, and underscores. + */ + bindingset[name] + predicate matchesName(string name) { + name.toUpperCase().regexpReplaceAll("[-_ ]", "") = getName() + } + + /** + * Holds if this algorithm is weak. + */ + abstract predicate isWeak(); +} + +/** + * A hashing algorithm such as `MD5` or `SHA512`. + */ +class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm { + string name; + boolean isWeak; + + HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) } + + override string getName() { result = name } + + override predicate isWeak() { isWeak = true } +} + +/** + * An encryption algorithm such as `DES` or `AES512`. + */ +class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm { + string name; + boolean isWeak; + + EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) } + + override string getName() { result = name } + + override predicate isWeak() { isWeak = true } +} + +/** + * A password hashing algorithm such as `PBKDF2` or `SCRYPT`. + */ +class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm { + string name; + boolean isWeak; + + PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) } + + override string getName() { result = name } + + override predicate isWeak() { isWeak = true } +} From 2c0df8e6562f0621128005b8bb766bcdce5d0ed6 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 1 Mar 2021 09:29:14 +0100 Subject: [PATCH 065/550] Python: Add MD5 tests --- .../frameworks/cryptodome/test_md5.py | 10 +++++++++ .../frameworks/cryptography/test_md5.py | 10 +++++++++ .../frameworks/stdlib/test_md5.py | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py new file mode 100644 index 00000000000..91aed63f393 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py @@ -0,0 +1,10 @@ +from Cryptodome.Hash import MD5 + +hasher = MD5.new(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + +hasher = MD5.new() # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py new file mode 100644 index 00000000000..ff997a49ae2 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py @@ -0,0 +1,10 @@ +from cryptography.hazmat.primitives import hashes + +from binascii import hexlify + + +hasher = hashes.Hash(hashes.MD5()) +hasher.update(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 + +digest = hasher.finalize() +print(hexlify(digest).decode('utf-8')) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py new file mode 100644 index 00000000000..e8d78961308 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py @@ -0,0 +1,21 @@ +import hashlib + + +hasher = hashlib.md5(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + +hasher = hashlib.md5() +hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + +hasher = hashlib.new('md5', b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + +hasher = hashlib.new('md5') +hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) From 1b2ed9d99a3e4b0dd194e7bde57df6a6a783c237 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 1 Mar 2021 10:21:09 +0100 Subject: [PATCH 066/550] Python: Align cryptodome tests --- .../library-tests/frameworks/crypto/README.md | 13 +++++++ .../frameworks/crypto/test_aes.py | 36 +++++++++++++++++++ .../frameworks/crypto/test_md5.py | 10 ++++++ .../frameworks/crypto/test_rc4.py | 30 ++++++++++++++++ .../frameworks/crypto/test_rsa.py | 2 +- 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 python/ql/test/experimental/library-tests/frameworks/crypto/README.md create mode 100644 python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/README.md b/python/ql/test/experimental/library-tests/frameworks/crypto/README.md new file mode 100644 index 00000000000..d3f680504be --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/README.md @@ -0,0 +1,13 @@ +These tests are a copy of the tests in [../cryptodome](../cryptodome) with `Cryptodome` replaced by `Crypto`. + +You can run the following command to update the tests: + +```sh +rm *.py && cp ../cryptodome/*.py . && sed -i -e 's/Cryptodome/Crypto/' *.py +``` + +The original [`pycrypto` PyPI package](https://pypi.org/project/pycrypto/) that provided the `Crypto` Python package has not been updated since 2013, so it is reasonable to assume that people will use the replacement [`pycryptodome` PyPI package](https://pypi.org/project/pycryptodome/) that has a (mostly) compatible API. + +The pycryptodome functionality is also available in the [`pycryptodomex` PyPI package](https://pypi.org/project/pycryptodomex/) which provides the `Cryptodome` Python package. + +To ensure our modeling actually covers _both_ ways of importing the same functionality, we have this convoluted test setup. diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py new file mode 100644 index 00000000000..e22a4c6634f --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py @@ -0,0 +1,36 @@ +# https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html +from Crypto.Cipher import AES + +import os + +key = os.urandom(256//8) +iv = os.urandom(16) + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + + +print("encrypt/decrypt") + +secret_message = b"secret message" + +padding_len = 16 - (len(secret_message) % 16) +padding = b"\0"*padding_len + +cipher = AES.new(key, AES.MODE_CBC, iv=iv) +# using separate .encrypt calls on individual lines does not work +whole_plantext = secret_message + padding +encrypted = cipher.encrypt(whole_plantext) + +print("encrypted={}".format(encrypted)) + +print() + +cipher = AES.new(key, AES.MODE_CBC, iv=iv) +decrypted = cipher.decrypt(encrypted) + +decrypted = decrypted[:-padding_len] + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py new file mode 100644 index 00000000000..c918094dd61 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py @@ -0,0 +1,10 @@ +from Crypto.Hash import MD5 + +hasher = MD5.new(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + +hasher = MD5.new() # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py new file mode 100644 index 00000000000..4ca870a7352 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py @@ -0,0 +1,30 @@ +# https://pycryptodome.readthedocs.io/en/latest/src/cipher/arc4.html +from Crypto.Cipher import ARC4 + +import os + +key = os.urandom(256//8) + + +# ------------------------------------------------------------------------------ +# encrypt/decrypt +# ------------------------------------------------------------------------------ + + + +print("encrypt/decrypt") + +secret_message = b"secret message" + +cipher = ARC4.new(key) +encrypted = cipher.encrypt(secret_message) + +print("encrypted={}".format(encrypted)) + +print() + +cipher = ARC4.new(key) +decrypted = cipher.decrypt(encrypted) + +print("decrypted={}".format(decrypted)) +assert decrypted == secret_message diff --git a/python/ql/test/library-tests/frameworks/crypto/test_rsa.py b/python/ql/test/library-tests/frameworks/crypto/test_rsa.py index 7d463e4f384..f27015f3812 100644 --- a/python/ql/test/library-tests/frameworks/crypto/test_rsa.py +++ b/python/ql/test/library-tests/frameworks/crypto/test_rsa.py @@ -58,8 +58,8 @@ print("signature={}".format(signature)) print() - verifier = pss.new(public_key) + hasher = SHA256.new(message) verifier.verify(hasher, signature) print("Signature verified (as expected)") From 23140dfb7634a909722ebd821005021618af9f2c Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 09:52:41 +0100 Subject: [PATCH 067/550] Python: Add CryptographicOperation modeling for Cryptodome --- .../semmle/python/frameworks/Cryptodome.qll | 112 +++++++++++++++++- .../frameworks/cryptodome/test_aes.py | 4 +- .../frameworks/cryptodome/test_md5.py | 8 +- .../frameworks/cryptodome/test_rc4.py | 4 +- .../frameworks/cryptodome/test_dsa.py | 12 +- .../frameworks/cryptodome/test_ec.py | 12 +- .../frameworks/cryptodome/test_rsa.py | 18 ++- 7 files changed, 139 insertions(+), 31 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Cryptodome.qll b/python/ql/src/semmle/python/frameworks/Cryptodome.qll index 19db03f2e8e..719deeb2d6b 100644 --- a/python/ql/src/semmle/python/frameworks/Cryptodome.qll +++ b/python/ql/src/semmle/python/frameworks/Cryptodome.qll @@ -17,7 +17,6 @@ private import semmle.python.ApiGraphs * See https://pycryptodome.readthedocs.io/en/latest/ */ private module CryptodomeModel { - // --------------------------------------------------------------------------- /** * A call to `Cryptodome.PublicKey.RSA.generate`/`Crypto.PublicKey.RSA.generate` * @@ -101,4 +100,115 @@ private module CryptodomeModel { // Note: There is not really a key-size argument, since it's always specified by the curve. override DataFlow::Node getKeySizeArg() { none() } } + + /** + * A cryptographic operation on an instance from the `Cipher` subpackage of `Cryptodome`/`Crypto`. + */ + class CryptodomeGenericCipherOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string methodName; + string cipherName; + + CryptodomeGenericCipherOperation() { + methodName in [ + "encrypt", "decrypt", "verify", "update", "hexverify", "encrypt_and_digest", + "decrypt_and_verify" + ] and + this = + API::moduleImport(["Crypto", "Cryptodome"]) + .getMember(["Cipher"]) + .getMember(cipherName) + .getMember("new") + .getReturn() + .getMember(methodName) + .getACall() + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) } + + override DataFlow::Node getAnInput() { + methodName = "encrypt" and + result in [this.getArg(0), this.getArgByName(["message", "plaintext"])] + or + methodName = "decrypt" and + result in [this.getArg(0), this.getArgByName("ciphertext")] + or + // for the following methods, method signatures can be found in + // https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html + methodName in ["update"] and + result in [this.getArg(0), this.getArgByName("data")] + or + methodName in ["verify"] and + result in [this.getArg(0), this.getArgByName(["mac_tag", "received_mac_tag"])] + or + methodName in ["hexverify"] and + result in [this.getArg(0), this.getArgByName("mac_tag_hex")] + or + methodName in ["encrypt_and_digest"] and + result in [this.getArg(0), this.getArgByName("plaintext")] + or + methodName in ["decrypt_and_verify"] and + result in [this.getArg(0), this.getArgByName("ciphertext")] + } + } + + /** + * A cryptographic operation on an instance from the `Signature` subpackage of `Cryptodome`/`Crypto`. + */ + class CryptodomeGenericSignatureOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string methodName; + string signatureName; + + CryptodomeGenericSignatureOperation() { + methodName in ["sign", "verify"] and + this = + API::moduleImport(["Crypto", "Cryptodome"]) + .getMember(["Signature"]) + .getMember(signatureName) + .getMember("new") + .getReturn() + .getMember(methodName) + .getACall() + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { + result.matchesName(signatureName) + } + + override DataFlow::Node getAnInput() { + methodName = "sign" and + result in [this.getArg(0), this.getArgByName("msg_hash")] // Cryptodome.Hash instance + or + methodName in ["verify"] and + ( + result in [this.getArg(0), this.getArgByName(["msg_hash"])] // Cryptodome.Hash instance + or + result in [this.getArg(1), this.getArgByName(["signature"])] + ) + } + } + + /** + * A cryptographic operation on an instance from the `Hash` subpackage of `Cryptodome`/`Crypto`. + */ + class CryptodomeGenericHashOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string hashName; + + CryptodomeGenericHashOperation() { + exists(API::Node hashModule | + hashModule = + API::moduleImport(["Crypto", "Cryptodome"]).getMember(["Hash"]).getMember(hashName) + | + this = hashModule.getMember("new").getACall() + or + this = hashModule.getMember("new").getReturn().getMember("update").getACall() + ) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py index 971fdadd327..9d233b6e33c 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py @@ -21,14 +21,14 @@ padding = b"\0"*padding_len cipher = AES.new(key, AES.MODE_CBC, iv=iv) # using separate .encrypt calls on individual lines does not work whole_plantext = secret_message + padding -encrypted = cipher.encrypt(whole_plantext) +encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext print("encrypted={}".format(encrypted)) print() cipher = AES.new(key, AES.MODE_CBC, iv=iv) -decrypted = cipher.decrypt(encrypted) +decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted decrypted = decrypted[:-padding_len] diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py index 91aed63f393..a1de2177e1b 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py @@ -1,10 +1,10 @@ from Cryptodome.Hash import MD5 -hasher = MD5.new(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = MD5.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) -hasher = MD5.new() # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=MD5 -hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 -hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +hasher = MD5.new() # $ CryptographicOperation CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py index a336dca6a49..4b2a2aeed37 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py @@ -17,14 +17,14 @@ print("encrypt/decrypt") secret_message = b"secret message" cipher = ARC4.new(key) -encrypted = cipher.encrypt(secret_message) +encrypted = cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message print("encrypted={}".format(encrypted)) print() cipher = ARC4.new(key) -decrypted = cipher.decrypt(encrypted) +decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted print("decrypted={}".format(decrypted)) assert decrypted == secret_message diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py index a33cf8c0944..617457da148 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py @@ -20,8 +20,8 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher print("signature={}".format(signature)) @@ -29,13 +29,13 @@ print() verifier = DSS.new(public_key, mode='fips-186-3') -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature print("Signature verified (as expected)") try: - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py b/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py index d3860bbb3b3..99512b682ac 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py @@ -17,8 +17,8 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher print("signature={}".format(signature)) @@ -26,13 +26,13 @@ print() verifier = DSS.new(public_key, mode='fips-186-3') -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature print("Signature verified (as expected)") try: - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py index cee261e5ebe..57cdb32f055 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py @@ -23,7 +23,7 @@ secret_message = b"secret message" encrypt_cipher = PKCS1_OAEP.new(public_key) -encrypted = encrypt_cipher.encrypt(secret_message) +encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message print("encrypted={}".format(encrypted)) @@ -31,9 +31,7 @@ print() decrypt_cipher = PKCS1_OAEP.new(private_key) -decrypted = decrypt_cipher.decrypt( - encrypted, -) +decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted print("decrypted={}".format(decrypted)) assert decrypted == secret_message @@ -51,8 +49,8 @@ message = b"message" signer = pss.new(private_key) -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher print("signature={}".format(signature)) @@ -60,14 +58,14 @@ print() verifier = pss.new(public_key) -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature print("Signature verified (as expected)") try: verifier = pss.new(public_key) - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") From f8254381f3335d6e794ddda40007137a30ce5886 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 09:53:09 +0100 Subject: [PATCH 068/550] Python: Add MISSING: CryptographicOperationAlgorithm annotations For RSA it's unclear what the algorithm name should even be. Signatures based on RSA private keys with PSS scheme is ok, but with pkcs#1 v1.5 they are weak/vulnerable. So clearly just putting RSA as the algorithm name is not enough information... and that problem is also why I wanted to do this commit separetely (to call extra atten to this). --- .../library-tests/frameworks/cryptodome/test_dsa.py | 6 +++--- .../library-tests/frameworks/cryptodome/test_ec.py | 4 ++-- .../library-tests/frameworks/cryptodome/test_rsa.py | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py index 617457da148..fecf02fcbf7 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_dsa.py @@ -21,7 +21,7 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message -signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=DSA print("signature={}".format(signature)) @@ -30,12 +30,12 @@ print() verifier = DSS.new(public_key, mode='fips-186-3') hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message -verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA print("Signature verified (as expected)") try: hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" - verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py b/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py index 99512b682ac..608aaecfdfd 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_ec.py @@ -18,7 +18,7 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message -signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=ECDSA print("signature={}".format(signature)) @@ -32,7 +32,7 @@ print("Signature verified (as expected)") try: hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" - verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=ECDSA raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py b/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py index 57cdb32f055..f6c2741c25d 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py +++ b/python/ql/test/library-tests/frameworks/cryptodome/test_rsa.py @@ -23,7 +23,7 @@ secret_message = b"secret message" encrypt_cipher = PKCS1_OAEP.new(public_key) -encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message +encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message # MISSING: CryptographicOperationAlgorithm=RSA-OAEP? print("encrypted={}".format(encrypted)) @@ -31,7 +31,7 @@ print() decrypt_cipher = PKCS1_OAEP.new(private_key) -decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted +decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted # MISSING: CryptographicOperationAlgorithm=RSA-OAEP? print("decrypted={}".format(decrypted)) assert decrypted == secret_message @@ -50,7 +50,7 @@ message = b"message" signer = pss.new(private_key) hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message -signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=RSA-PSS? print("signature={}".format(signature)) @@ -59,13 +59,13 @@ print() verifier = pss.new(public_key) hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message -verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS? print("Signature verified (as expected)") try: verifier = pss.new(public_key) hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" - verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS? raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") From bf6f5074c24ff8064a3609b3016b23d75a41def7 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 10:06:06 +0100 Subject: [PATCH 069/550] Python: Port cryptodome tests to crypto I don't know if this is really a smart test-setup... I feel a bit stupid when doing this xD --- .../frameworks/crypto/test_aes.py | 4 ++-- .../frameworks/crypto/test_md5.py | 8 ++++---- .../frameworks/crypto/test_rc4.py | 4 ++-- .../frameworks/crypto/test_dsa.py | 12 ++++++------ .../library-tests/frameworks/crypto/test_ec.py | 12 ++++++------ .../frameworks/crypto/test_rsa.py | 18 ++++++++---------- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py index e22a4c6634f..e838c1aaf2b 100644 --- a/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py @@ -21,14 +21,14 @@ padding = b"\0"*padding_len cipher = AES.new(key, AES.MODE_CBC, iv=iv) # using separate .encrypt calls on individual lines does not work whole_plantext = secret_message + padding -encrypted = cipher.encrypt(whole_plantext) +encrypted = cipher.encrypt(whole_plantext) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=whole_plantext print("encrypted={}".format(encrypted)) print() cipher = AES.new(key, AES.MODE_CBC, iv=iv) -decrypted = cipher.decrypt(encrypted) +decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted decrypted = decrypted[:-padding_len] diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py index c918094dd61..7004638c065 100644 --- a/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py @@ -1,10 +1,10 @@ from Crypto.Hash import MD5 -hasher = MD5.new(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = MD5.new(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) -hasher = MD5.new() # $ MISSING: CryptographicOperation CryptographicOperationAlgorithm=MD5 -hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 -hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +hasher = MD5.new() # $ CryptographicOperation CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py index 4ca870a7352..56b65aac150 100644 --- a/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py +++ b/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py @@ -17,14 +17,14 @@ print("encrypt/decrypt") secret_message = b"secret message" cipher = ARC4.new(key) -encrypted = cipher.encrypt(secret_message) +encrypted = cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message print("encrypted={}".format(encrypted)) print() cipher = ARC4.new(key) -decrypted = cipher.decrypt(encrypted) +decrypted = cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted print("decrypted={}".format(decrypted)) assert decrypted == secret_message diff --git a/python/ql/test/library-tests/frameworks/crypto/test_dsa.py b/python/ql/test/library-tests/frameworks/crypto/test_dsa.py index a6c2c081845..7204ceb5917 100644 --- a/python/ql/test/library-tests/frameworks/crypto/test_dsa.py +++ b/python/ql/test/library-tests/frameworks/crypto/test_dsa.py @@ -20,8 +20,8 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=DSA print("signature={}".format(signature)) @@ -29,13 +29,13 @@ print() verifier = DSS.new(public_key, mode='fips-186-3') -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA print("Signature verified (as expected)") try: - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=DSA raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/crypto/test_ec.py b/python/ql/test/library-tests/frameworks/crypto/test_ec.py index 2b482b4aa4e..565c779f610 100644 --- a/python/ql/test/library-tests/frameworks/crypto/test_ec.py +++ b/python/ql/test/library-tests/frameworks/crypto/test_ec.py @@ -17,8 +17,8 @@ message = b"message" signer = DSS.new(private_key, mode='fips-186-3') -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=ECDSA print("signature={}".format(signature)) @@ -26,13 +26,13 @@ print() verifier = DSS.new(public_key, mode='fips-186-3') -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature print("Signature verified (as expected)") try: - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=ECDSA raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") diff --git a/python/ql/test/library-tests/frameworks/crypto/test_rsa.py b/python/ql/test/library-tests/frameworks/crypto/test_rsa.py index f27015f3812..c93763b9bc8 100644 --- a/python/ql/test/library-tests/frameworks/crypto/test_rsa.py +++ b/python/ql/test/library-tests/frameworks/crypto/test_rsa.py @@ -23,7 +23,7 @@ secret_message = b"secret message" encrypt_cipher = PKCS1_OAEP.new(public_key) -encrypted = encrypt_cipher.encrypt(secret_message) +encrypted = encrypt_cipher.encrypt(secret_message) # $ CryptographicOperation CryptographicOperationInput=secret_message # MISSING: CryptographicOperationAlgorithm=RSA-OAEP? print("encrypted={}".format(encrypted)) @@ -31,9 +31,7 @@ print() decrypt_cipher = PKCS1_OAEP.new(private_key) -decrypted = decrypt_cipher.decrypt( - encrypted, -) +decrypted = decrypt_cipher.decrypt(encrypted) # $ CryptographicOperation CryptographicOperationInput=encrypted # MISSING: CryptographicOperationAlgorithm=RSA-OAEP? print("decrypted={}".format(decrypted)) assert decrypted == secret_message @@ -51,8 +49,8 @@ message = b"message" signer = pss.new(private_key) -hasher = SHA256.new(message) -signature = signer.sign(hasher) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +signature = signer.sign(hasher) # $ CryptographicOperation CryptographicOperationInput=hasher # MISSING: CryptographicOperationAlgorithm=RSA-PSS? print("signature={}".format(signature)) @@ -60,14 +58,14 @@ print() verifier = pss.new(public_key) -hasher = SHA256.new(message) -verifier.verify(hasher, signature) +hasher = SHA256.new(message) # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=message +verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS? print("Signature verified (as expected)") try: verifier = pss.new(public_key) - hasher = SHA256.new(b"other message") - verifier.verify(hasher, signature) + hasher = SHA256.new(b"other message") # $ CryptographicOperation CryptographicOperationAlgorithm=SHA256 CryptographicOperationInput=b"other message" + verifier.verify(hasher, signature) # $ CryptographicOperation CryptographicOperationInput=hasher CryptographicOperationInput=signature # MISSING: CryptographicOperationAlgorithm=RSA-PSS? raise Exception("Signature verified (unexpected)") except ValueError: print("Signature mismatch (as expected)") From c5f826580ba20b5ad6672f5e0beaada1ec1776cd Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 13:32:13 +0100 Subject: [PATCH 070/550] Python: Model encrypt/decrypt in cryptography package I introduced a InternalTypeTracking module, since the type-tracking code got so verbose, that it was impossible to get an overview of the relevant predicates. (this means the "first" type-tracking predicate that is usually private, cannot be marked private anymore, since it needs to be exposed in the private module. --- .../semmle/python/frameworks/Cryptography.qll | 161 ++++++++++++++++++ .../frameworks/cryptography/test_aes.py | 6 +- .../frameworks/cryptography/test_rc4.py | 4 +- 3 files changed, 166 insertions(+), 5 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Cryptography.qll b/python/ql/src/semmle/python/frameworks/Cryptography.qll index 266d41897d3..0bb5861c48f 100644 --- a/python/ql/src/semmle/python/frameworks/Cryptography.qll +++ b/python/ql/src/semmle/python/frameworks/Cryptography.qll @@ -169,4 +169,165 @@ private module CryptographyModel { // Note: There is not really a key-size argument, since it's always specified by the curve. override DataFlow::Node getKeySizeArg() { none() } } + + /** Provides models for the `cryptography.hazmat.primitives.ciphers` package */ + private module Ciphers { + /** Gets a reference to a `cryptography.hazmat.primitives.ciphers.algorithms` Class */ + API::Node algorithmClassRef(string algorithmName) { + result = + API::moduleImport("cryptography") + .getMember("hazmat") + .getMember("primitives") + .getMember("ciphers") + .getMember("algorithms") + .getMember(algorithmName) + } + + /** + * Internal module making it easy to hide verbose type-tracking helpers. + * + * These turned out to be so verbose, that it was impossible to get an overview of + * the relevant predicates without hiding them away. + */ + private module InternalTypeTracking { + /** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */ + DataFlow::LocalSourceNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) { + t.start() and + exists(DataFlow::CallCfgNode call | result = call | + call = + API::moduleImport("cryptography") + .getMember("hazmat") + .getMember("primitives") + .getMember("ciphers") + .getMember("Cipher") + .getACall() and + algorithmClassRef(algorithmName).getReturn().getAUse() in [ + call.getArg(0), call.getArgByName("algorithm") + ] + ) + or + // Due to bad performance when using normal setup with `cipherInstance(t2, algorithmName).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + cipherInstance_first_join(t2, algorithmName, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate cipherInstance_first_join( + DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, + DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(cipherInstance(t2, algorithmName), res, summary) + } + + /** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */ + DataFlow::LocalSourceNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) { + t.start() and + exists(DataFlow::AttrRead attr | + result.(DataFlow::CallCfgNode).getFunction() = attr and + attr.getAttributeName() = "encryptor" and + attr.getObject() = cipherInstance(algorithmName) + ) + or + // Due to bad performance when using normal setup with `cipherEncryptor(t2, algorithmName).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + cipherEncryptor_first_join(t2, algorithmName, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate cipherEncryptor_first_join( + DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, + DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(cipherEncryptor(t2, algorithmName), res, summary) + } + + /** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */ + DataFlow::LocalSourceNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) { + t.start() and + exists(DataFlow::AttrRead attr | + result.(DataFlow::CallCfgNode).getFunction() = attr and + attr.getAttributeName() = "decryptor" and + attr.getObject() = cipherInstance(algorithmName) + ) + or + // Due to bad performance when using normal setup with `cipherDecryptor(t2, algorithmName).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + cipherDecryptor_first_join(t2, algorithmName, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate cipherDecryptor_first_join( + DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, + DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(cipherDecryptor(t2, algorithmName), res, summary) + } + } + + private import InternalTypeTracking + + /** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */ + DataFlow::Node cipherInstance(string algorithmName) { + cipherInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result) + } + + /** + * Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. + * + * You obtain an encryptor by using the `encryptor()` method on a Cipher instance. + */ + DataFlow::Node cipherEncryptor(string algorithmName) { + cipherEncryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result) + } + + /** + * Gets a reference to the decryptor of a Cipher instance using algorithm with `algorithmName`. + * + * You obtain an decryptor by using the `decryptor()` method on a Cipher instance. + */ + DataFlow::Node cipherDecryptor(string algorithmName) { + cipherDecryptor(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result) + } + + /** + * An encrypt or decrypt operation from `cryptography.hazmat.primitives.ciphers`. + */ + class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string algorithmName; + + CryptographyGenericCipherOperation() { + exists(DataFlow::AttrRead attr | + this.getFunction() = attr and + attr.getAttributeName() = ["update", "update_into"] and + ( + attr.getObject() = cipherEncryptor(algorithmName) + or + attr.getObject() = cipherDecryptor(algorithmName) + ) + ) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { + result.matchesName(algorithmName) + } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] } + } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py index 7bd7040d0b4..548eea5c32a 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py @@ -22,8 +22,8 @@ padding = b"\0"*padding_len encryptor = cipher.encryptor() print(padding_len) -encrypted = encryptor.update(secret_message) -encrypted += encryptor.update(padding) +encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=secret_message +encrypted += encryptor.update(padding) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=padding encrypted += encryptor.finalize() print("encrypted={}".format(encrypted)) @@ -31,7 +31,7 @@ print("encrypted={}".format(encrypted)) print() decryptor = cipher.decryptor() -decrypted = decryptor.update(encrypted) +decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=AES CryptographicOperationInput=encrypted decrypted += decryptor.finalize() decrypted = decrypted[:-padding_len] diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py index 0d3fbcc320c..3a74d3e9dd2 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py @@ -17,7 +17,7 @@ print("encrypt/decrypt") secret_message = b"secret message" encryptor = cipher.encryptor() -encrypted = encryptor.update(secret_message) +encrypted = encryptor.update(secret_message) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=secret_message encrypted += encryptor.finalize() print("encrypted={}".format(encrypted)) @@ -25,7 +25,7 @@ print("encrypted={}".format(encrypted)) print() decryptor = cipher.decryptor() -decrypted = decryptor.update(encrypted) +decrypted = decryptor.update(encrypted) # $ CryptographicOperation CryptographicOperationAlgorithm=ARC4 CryptographicOperationInput=encrypted decrypted += decryptor.finalize() print("decrypted={}".format(decrypted)) From fa88f22453e9330f6224e8e61520e5e2e13c9ce8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 13:54:36 +0100 Subject: [PATCH 071/550] Python: Model hashing operations in `cryptography` package --- .../semmle/python/frameworks/Cryptography.qll | 77 +++++++++++++++++++ .../frameworks/cryptography/test_md5.py | 2 +- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/frameworks/Cryptography.qll b/python/ql/src/semmle/python/frameworks/Cryptography.qll index 0bb5861c48f..bc7bdc0109f 100644 --- a/python/ql/src/semmle/python/frameworks/Cryptography.qll +++ b/python/ql/src/semmle/python/frameworks/Cryptography.qll @@ -330,4 +330,81 @@ private module CryptographyModel { override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] } } } + + /** Provides models for the `cryptography.hazmat.primitives.hashes` package */ + private module Hashes { + /** + * Gets a reference to a `cryptography.hazmat.primitives.hashes` class, representing + * a hashing algorithm. + */ + API::Node algorithmClassRef(string algorithmName) { + result = + API::moduleImport("cryptography") + .getMember("hazmat") + .getMember("primitives") + .getMember("hashes") + .getMember(algorithmName) + } + + /** Gets a reference to a Hash instance using algorithm with `algorithmName`. */ + private DataFlow::LocalSourceNode hashInstance(DataFlow::TypeTracker t, string algorithmName) { + t.start() and + exists(DataFlow::CallCfgNode call | result = call | + call = + API::moduleImport("cryptography") + .getMember("hazmat") + .getMember("primitives") + .getMember("hashes") + .getMember("Hash") + .getACall() and + algorithmClassRef(algorithmName).getReturn().getAUse() in [ + call.getArg(0), call.getArgByName("algorithm") + ] + ) + or + // Due to bad performance when using normal setup with `hashInstance(t2, algorithmName).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + hashInstance_first_join(t2, algorithmName, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate hashInstance_first_join( + DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, + DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(hashInstance(t2, algorithmName), res, summary) + } + + /** Gets a reference to a Hash instance using algorithm with `algorithmName`. */ + DataFlow::Node hashInstance(string algorithmName) { + hashInstance(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result) + } + + /** + * An hashing operation from `cryptography.hazmat.primitives.hashes`. + */ + class CryptographyGenericHashOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string algorithmName; + + CryptographyGenericHashOperation() { + exists(DataFlow::AttrRead attr | + this.getFunction() = attr and + attr.getAttributeName() = "update" and + attr.getObject() = hashInstance(algorithmName) + ) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { + result.matchesName(algorithmName) + } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] } + } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py index ff997a49ae2..14cc3322020 100644 --- a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py +++ b/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py @@ -4,7 +4,7 @@ from binascii import hexlify hasher = hashes.Hash(hashes.MD5()) -hasher.update(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 digest = hasher.finalize() print(hexlify(digest).decode('utf-8')) From 7ffbfa80438731956f999832c2fbc356eec357c2 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 2 Mar 2021 16:45:09 +0100 Subject: [PATCH 072/550] Python: Expand stdlib md5 tests with keyword-arguments --- .../library-tests/frameworks/stdlib/test_md5.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py index e8d78961308..200a68662e4 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py @@ -5,6 +5,10 @@ hasher = hashlib.md5(b"secret message") # $ MISSING: CryptographicOperation Cryp print(hasher.hexdigest()) +hasher = hashlib.md5(string=b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + hasher = hashlib.md5() hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 @@ -15,6 +19,10 @@ hasher = hashlib.new('md5', b"secret message") # $ MISSING: CryptographicOperati print(hasher.hexdigest()) +hasher = hashlib.new('md5', data=b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +print(hasher.hexdigest()) + + hasher = hashlib.new('md5') hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 From 1616975e0682f7670911c58fe5bc96893d344d25 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 3 Mar 2021 10:06:01 +0100 Subject: [PATCH 073/550] Python: Model `hashlib` from standard library --- .../src/semmle/python/frameworks/Stdlib.qll | 127 ++++++++++++++++++ .../frameworks/stdlib/test_md5.py | 16 +-- 2 files changed, 135 insertions(+), 8 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index 01df130d0db..5f4a2d22adb 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -866,6 +866,133 @@ private module Stdlib { } } +// --------------------------------------------------------------------------- +// hashlib +// --------------------------------------------------------------------------- +/** Gets a call to `hashlib.new` with `algorithmName` as the first argument. */ +private DataFlow::CallCfgNode hashlibNewCall(string algorithmName) { + exists(DataFlow::Node nameArg | + result = API::moduleImport("hashlib").getMember("new").getACall() and + nameArg in [result.getArg(0), result.getArgByName("name")] and + exists(StrConst str | + DataFlow::exprNode(str).(DataFlow::LocalSourceNode).flowsTo(nameArg) and + algorithmName = str.getText() + ) + ) +} + +/** Gets a reference to the result of calling `hashlib.new` with `algorithmName` as the first argument. */ +private DataFlow::LocalSourceNode hashlibNewResult(DataFlow::TypeTracker t, string algorithmName) { + t.start() and + result = hashlibNewCall(algorithmName) + or + // Due to bad performance when using normal setup with `hashlibNewResult(t2, algorithmName).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + hashlibNewResult_first_join(t2, algorithmName, result, summary) and + t = t2.append(summary) + ) + ) +} + +pragma[nomagic] +private predicate hashlibNewResult_first_join( + DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, DataFlow::StepSummary summary +) { + DataFlow::StepSummary::step(hashlibNewResult(t2, algorithmName), res, summary) +} + +/** Gets a reference to the result of calling `hashlib.new` with `algorithmName` as the first argument. */ +DataFlow::Node hashlibNewResult(string algorithmName) { + hashlibNewResult(DataFlow::TypeTracker::end(), algorithmName).flowsTo(result) +} + +/** + * A hashing operation by supplying initial data when calling the `hashlib.new` function. + */ +class HashlibNewCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode { + string hashName; + + HashlibNewCall() { + this = hashlibNewCall(hashName) and + exists([this.getArg(1), this.getArgByName("data")]) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) } + + override DataFlow::Node getAnInput() { result in [this.getArg(1), this.getArgByName("data")] } +} + +/** + * A hashing operation by using the `update` method on the result of calling the `hashlib.new` function. + */ +class HashlibNewUpdateCall extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string hashName; + + HashlibNewUpdateCall() { + exists(DataFlow::AttrRead attr | + attr.getObject() = hashlibNewResult(hashName) and + this.getFunction() = attr and + attr.getAttributeName() = "update" + ) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } +} + +/** + * A hashing operation from the `hashlib` package using one of the predefined classes + * (such as `hashlib.md5`). `hashlib.new` is not included, since it is handled by + * `HashlibNewCall` and `HashlibNewUpdateCall`. + */ +abstract class HashlibGenericHashOperation extends Cryptography::CryptographicOperation::Range, + DataFlow::CallCfgNode { + string hashName; + API::Node hashClass; + + bindingset[this] + HashlibGenericHashOperation() { + not hashName = "new" and + hashClass = API::moduleImport("hashlib").getMember(hashName) + } + + override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) } +} + +/** + * A hashing operation from the `hashlib` package using one of the predefined classes + * (such as `hashlib.md5`), by calling its' `update` mehtod. + */ +class HashlibHashClassUpdateCall extends HashlibGenericHashOperation { + HashlibHashClassUpdateCall() { this = hashClass.getReturn().getMember("update").getACall() } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } +} + +/** + * A hashing operation from the `hashlib` package using one of the predefined classes + * (such as `hashlib.md5`), by passing data to when instantiating the class. + */ +class HashlibDataPassedToHashClass extends HashlibGenericHashOperation { + HashlibDataPassedToHashClass() { + // we only want to model calls to classes such as `hashlib.md5()` if initial data + // is passed as an argument + this = hashClass.getACall() and + exists([this.getArg(0), this.getArgByName("string")]) + } + + override DataFlow::Node getAnInput() { + result = this.getArg(0) + or + // in Python 3.9, you are allowed to use `hashlib.md5(string=)`. + result = this.getArgByName("string") + } +} + // --------------------------------------------------------------------------- // OTHER // --------------------------------------------------------------------------- diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py index 200a68662e4..b4fae98878f 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py @@ -1,29 +1,29 @@ import hashlib -hasher = hashlib.md5(b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = hashlib.md5(b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) -hasher = hashlib.md5(string=b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = hashlib.md5(string=b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) hasher = hashlib.md5() -hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 -hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) -hasher = hashlib.new('md5', b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = hashlib.new('md5', b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) -hasher = hashlib.new('md5', data=b"secret message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 +hasher = hashlib.new('md5', data=b"secret message") # $ CryptographicOperation CryptographicOperationInput=b"secret message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) hasher = hashlib.new('md5') -hasher.update(b"secret") # $ MISSING: CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 -hasher.update(b" message") # $ MISSING: CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 +hasher.update(b"secret") # $ CryptographicOperation CryptographicOperationInput=b"secret" CryptographicOperationAlgorithm=MD5 +hasher.update(b" message") # $ CryptographicOperation CryptographicOperationInput=b" message" CryptographicOperationAlgorithm=MD5 print(hasher.hexdigest()) From 59edd18c3494a53888be947f4283ae8ac3043983 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 9 Apr 2021 11:22:26 +0200 Subject: [PATCH 074/550] Python: Move framework test-files out of experimental This PR was rebased on newest main, but was written a long time ago when all the framework test-files were still in experimental. I have not re-written my local git-history, since there are MANY updates to those files (and I dare not risk it). --- .../{experimental => }/library-tests/frameworks/crypto/README.md | 0 .../library-tests/frameworks/crypto/test_aes.py | 0 .../library-tests/frameworks/crypto/test_md5.py | 0 .../library-tests/frameworks/crypto/test_rc4.py | 0 .../library-tests/frameworks/cryptodome/test_aes.py | 0 .../library-tests/frameworks/cryptodome/test_md5.py | 0 .../library-tests/frameworks/cryptodome/test_rc4.py | 0 .../library-tests/frameworks/cryptography/test_aes.py | 0 .../library-tests/frameworks/cryptography/test_md5.py | 0 .../library-tests/frameworks/cryptography/test_rc4.py | 0 .../library-tests/frameworks/stdlib/test_md5.py | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/README.md (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_aes.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_md5.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/crypto/test_rc4.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_aes.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_md5.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptodome/test_rc4.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_aes.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_md5.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/cryptography/test_rc4.py (100%) rename python/ql/test/{experimental => }/library-tests/frameworks/stdlib/test_md5.py (100%) diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/README.md b/python/ql/test/library-tests/frameworks/crypto/README.md similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/crypto/README.md rename to python/ql/test/library-tests/frameworks/crypto/README.md diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py b/python/ql/test/library-tests/frameworks/crypto/test_aes.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_aes.py rename to python/ql/test/library-tests/frameworks/crypto/test_aes.py diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py b/python/ql/test/library-tests/frameworks/crypto/test_md5.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_md5.py rename to python/ql/test/library-tests/frameworks/crypto/test_md5.py diff --git a/python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py b/python/ql/test/library-tests/frameworks/crypto/test_rc4.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/crypto/test_rc4.py rename to python/ql/test/library-tests/frameworks/crypto/test_rc4.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py b/python/ql/test/library-tests/frameworks/cryptodome/test_aes.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_aes.py rename to python/ql/test/library-tests/frameworks/cryptodome/test_aes.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py b/python/ql/test/library-tests/frameworks/cryptodome/test_md5.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_md5.py rename to python/ql/test/library-tests/frameworks/cryptodome/test_md5.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py b/python/ql/test/library-tests/frameworks/cryptodome/test_rc4.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py rename to python/ql/test/library-tests/frameworks/cryptodome/test_rc4.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py b/python/ql/test/library-tests/frameworks/cryptography/test_aes.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_aes.py rename to python/ql/test/library-tests/frameworks/cryptography/test_aes.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py b/python/ql/test/library-tests/frameworks/cryptography/test_md5.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_md5.py rename to python/ql/test/library-tests/frameworks/cryptography/test_md5.py diff --git a/python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py b/python/ql/test/library-tests/frameworks/cryptography/test_rc4.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py rename to python/ql/test/library-tests/frameworks/cryptography/test_rc4.py diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py b/python/ql/test/library-tests/frameworks/stdlib/test_md5.py similarity index 100% rename from python/ql/test/experimental/library-tests/frameworks/stdlib/test_md5.py rename to python/ql/test/library-tests/frameworks/stdlib/test_md5.py From 56c409737d16dbc26896fe2a25b2166d9fcea22e Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 11:47:15 +0200 Subject: [PATCH 075/550] Python: Port py/weak-cryptographic-algorithm The other query (py/weak-sensitive-data-hashing) is added in future commit --- .../CWE-327/BrokenCryptoAlgorithm.qhelp | 22 ++++++++++---- .../Security/CWE-327/BrokenCryptoAlgorithm.ql | 29 +++++++------------ .../CWE-327/BrokenCryptoAlgorithm.ql | 28 ++++++++++++++++++ .../BrokenCryptoAlgorithm.expected | 10 ++----- .../CWE-327-BrokenCryptoAlgorithm/README.md | 3 ++ .../TestNode.expected | 12 -------- .../CWE-327-BrokenCryptoAlgorithm/TestNode.ql | 9 ------ .../CWE-327-BrokenCryptoAlgorithm/options | 1 - .../test_cryptodome.py | 13 +++++++++ .../test_cryptography.py | 21 +++++++++----- .../test_pycrypto.py | 8 ----- 11 files changed, 87 insertions(+), 69 deletions(-) create mode 100644 python/ql/src/experimental/Security-old-dataflow/CWE-327/BrokenCryptoAlgorithm.ql create mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/README.md delete mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected delete mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql delete mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options create mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptodome.py delete mode 100644 python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp index ba5ab4d10c1..5d18f6dd5d3 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp @@ -15,22 +15,28 @@ secure than it appears to be.

    +

    + This query alerts on any use of a weak cryptographic algorithm, that is + not a hashing algorithm. Use of broken or weak cryptographic hash + functions are handled by the + py/weak-sensitive-data-hashing query. +

    +

    Ensure that you use a strong, modern cryptographic - algorithm. Use at least AES-128 or RSA-2048 for - encryption, and SHA-2 or SHA-3 for secure hashing. + algorithm, such as AES-128 or RSA-2048.

    - The following code uses the pycrypto + The following code uses the pycryptodome library to encrypt some secret data. When you create a cipher using - pycrypto you must specify the encryption + pycryptodome 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 AES, which is a stronger modern algorithm. @@ -39,8 +45,12 @@

    - WARNING: Although the second example above is more robust, - pycrypto is no longer actively maintained so we recommend using cryptography instead. + NOTICE: the original + pycrypto + PyPI package that provided the Crypto module is not longer + actively maintained, so you should use the + pycryptodome + PyPI package instead (which has a compatible API).

    diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql index 36064dc0386..66f07ea2c7b 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql @@ -1,7 +1,7 @@ /** * @name Use of a broken or weak cryptographic algorithm * @description Using broken or weak cryptographic algorithms can compromise security. - * @kind path-problem + * @kind problem * @problem.severity warning * @precision high * @id py/weak-cryptographic-algorithm @@ -10,21 +10,14 @@ */ import python -import semmle.python.security.Paths -import semmle.python.security.SensitiveData -import semmle.python.security.Crypto +import semmle.python.Concepts -class BrokenCryptoConfiguration extends TaintTracking::Configuration { - BrokenCryptoConfiguration() { this = "Broken crypto configuration" } - - override predicate isSource(TaintTracking::Source source) { - source instanceof SensitiveDataSource - } - - override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } -} - -from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink -where config.hasFlowPath(src, sink) -select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.", - src.getSource(), "Sensitive data" +from Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm +where + algorithm = operation.getAlgorithm() and + algorithm.isWeak() and + not algorithm instanceof Cryptography::HashingAlgorithm and // handled by `py/weak-sensitive-data-hashing` + not algorithm instanceof Cryptography::PasswordHashingAlgorithm // handled by `py/weak-sensitive-data-hashing` +select operation, + "The cryptographic algorithm " + algorithm.getName() + + " is broken or weak, and should not be used." diff --git a/python/ql/src/experimental/Security-old-dataflow/CWE-327/BrokenCryptoAlgorithm.ql b/python/ql/src/experimental/Security-old-dataflow/CWE-327/BrokenCryptoAlgorithm.ql new file mode 100644 index 00000000000..3c183c3b4f9 --- /dev/null +++ b/python/ql/src/experimental/Security-old-dataflow/CWE-327/BrokenCryptoAlgorithm.ql @@ -0,0 +1,28 @@ +/** + * @name OLD QUERY: Use of a broken or weak cryptographic algorithm + * @description Using broken or weak cryptographic algorithms can compromise security. + * @kind path-problem + * @problem.severity warning + * @id py/old/weak-cryptographic-algorithm + * @deprecated + */ + +import python +import semmle.python.security.Paths +import semmle.python.security.SensitiveData +import semmle.python.security.Crypto + +class BrokenCryptoConfiguration extends TaintTracking::Configuration { + BrokenCryptoConfiguration() { this = "Broken crypto configuration" } + + override predicate isSource(TaintTracking::Source source) { + source instanceof SensitiveDataSource + } + + override predicate isSink(TaintTracking::Sink sink) { sink instanceof WeakCryptoSink } +} + +from BrokenCryptoConfiguration config, TaintedPathSource src, TaintedPathSink sink +where config.hasFlowPath(src, sink) +select sink.getSink(), src, sink, "$@ is used in a broken or weak cryptographic algorithm.", + src.getSource(), "Sensitive data" diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected index 8a680bc702c..acae4cbfb06 100644 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected +++ b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/BrokenCryptoAlgorithm.expected @@ -1,8 +1,2 @@ -edges -| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password | -| test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password | -| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password | -| test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password | -#select -| test_cryptography.py:8:29:8:37 | dangerous | test_cryptography.py:5:17:5:30 | a password | test_cryptography.py:8:29:8:37 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:5:17:5:30 | get_password() | Sensitive data | -| test_pycrypto.py:7:27:7:35 | dangerous | test_pycrypto.py:5:17:5:30 | a password | test_pycrypto.py:7:27:7:35 | a password | $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:5:17:5:30 | get_password() | Sensitive data | +| test_cryptodome.py:11:13:11:42 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. | +| test_cryptography.py:13:13:13:44 | ControlFlowNode for Attribute() | The cryptographic algorithm ARC4 is broken or weak, and should not be used. | diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/README.md b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/README.md new file mode 100644 index 00000000000..9c6e429aefc --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/README.md @@ -0,0 +1,3 @@ +Note that the tests in this directory are very shallow, and simply show that the query is able to produce alerts. + +More in-depth tests can be found for the individual frameworks that we have modeled `Cryptography::CryptographicOperation` for. diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected deleted file mode 100644 index c79f7a9d195..00000000000 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.expected +++ /dev/null @@ -1,12 +0,0 @@ -| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:6:14:6:27 | test_pycrypto.py:6 | test_pycrypto.py:6:14:6:27 | Attribute() | | -| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:7:12:7:17 | test_pycrypto.py:7 | test_pycrypto.py:7:12:7:17 | cipher | | -| Taint cryptography.Cipher.RC4 | test_cryptography.py:6:14:6:47 | test_cryptography.py:6 | test_cryptography.py:6:14:6:47 | Cipher() | | -| Taint cryptography.Cipher.RC4 | test_cryptography.py:7:17:7:22 | test_cryptography.py:7 | test_cryptography.py:7:17:7:22 | cipher | | -| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:17:7:34 | test_cryptography.py:7 | test_cryptography.py:7:17:7:34 | Attribute() | | -| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:12:8:20 | test_cryptography.py:8 | test_cryptography.py:8:12:8:20 | encryptor | | -| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:42:8:50 | test_cryptography.py:8 | test_cryptography.py:8:42:8:50 | encryptor | | -| Taint sensitive.data.password | test_cryptography.py:5:17:5:30 | test_cryptography.py:5 | test_cryptography.py:5:17:5:30 | get_password() | | -| Taint sensitive.data.password | test_cryptography.py:8:29:8:37 | test_cryptography.py:8 | test_cryptography.py:8:29:8:37 | dangerous | | -| Taint sensitive.data.password | test_cryptography.py:8:42:8:50 | test_cryptography.py:8 | test_cryptography.py:8:42:8:50 | encryptor | | -| Taint sensitive.data.password | test_pycrypto.py:5:17:5:30 | test_pycrypto.py:5 | test_pycrypto.py:5:17:5:30 | get_password() | | -| Taint sensitive.data.password | test_pycrypto.py:7:27:7:35 | test_pycrypto.py:7 | test_pycrypto.py:7:27:7:35 | dangerous | | diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql deleted file mode 100644 index 420ed8bb38e..00000000000 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/TestNode.ql +++ /dev/null @@ -1,9 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import python -import semmle.python.security.SensitiveData -import semmle.python.security.Crypto - -from TaintedNode n, AstNode src -where src = n.getAstNode() and src.getLocation().getFile().getAbsolutePath().matches("%test%") -select "Taint " + n.getTaintKind(), n.getLocation(), src, n.getContext() diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options deleted file mode 100644 index 492768b3481..00000000000 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/options +++ /dev/null @@ -1 +0,0 @@ -semmle-extractor-options: -p ../lib/ --max-import-depth=3 diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptodome.py b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptodome.py new file mode 100644 index 00000000000..7e5fe780d61 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptodome.py @@ -0,0 +1,13 @@ +# snippet from python/ql/test/experimental/library-tests/frameworks/cryptodome/test_rc4.py +from Cryptodome.Cipher import ARC4 + +import os + +key = os.urandom(256//8) + +secret_message = b"secret message" + +cipher = ARC4.new(key) +encrypted = cipher.encrypt(secret_message) # NOT OK + +print(secret_message, encrypted) diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py index da6c3a4c3aa..2698f9c30d1 100644 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py +++ b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_cryptography.py @@ -1,9 +1,16 @@ -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms -from secrets_store import get_password +# snippet from python/ql/test/experimental/library-tests/frameworks/cryptography/test_rc4.py +from cryptography.hazmat.primitives.ciphers import algorithms, Cipher +import os -def get_badly_encrypted_password(): - dangerous = get_password() - cipher = Cipher(algorithms.ARC4(key), _, _) - encryptor = cipher.encryptor() - return encryptor.update(dangerous) + encryptor.finalize() +key = os.urandom(256//8) +algorithm = algorithms.ARC4(key) +cipher = Cipher(algorithm, mode=None) + +secret_message = b"secret message" + +encryptor = cipher.encryptor() +encrypted = encryptor.update(secret_message) # NOT OK +encrypted += encryptor.finalize() + +print(secret_message, encrypted) diff --git a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py b/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py deleted file mode 100644 index d1dda03b4df..00000000000 --- a/python/ql/test/query-tests/Security/CWE-327-BrokenCryptoAlgorithm/test_pycrypto.py +++ /dev/null @@ -1,8 +0,0 @@ -from Crypto.Cipher import ARC4 -from secrets_store import get_password - -def get_badly_encrypted_password(): - dangerous = get_password() - cipher = ARC4.new(_, _) - return cipher.encrypt(dangerous) - From 794a86a6b07709810ec43771e99d5f0fdaff9a68 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 11:50:21 +0200 Subject: [PATCH 076/550] Python: Add SensitiveDataSource --- .../dataflow/new/SensitiveDataSources.qll | 66 +++++++++++++++++++ .../TestSensitiveDataSources.expected | 0 .../TestSensitiveDataSources.ql | 20 ++++++ .../dataflow/sensitive-data/test.py | 21 ++++++ 4 files changed, 107 insertions(+) create mode 100644 python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll create mode 100644 python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.expected create mode 100644 python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.ql create mode 100644 python/ql/test/experimental/dataflow/sensitive-data/test.py diff --git a/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll b/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll new file mode 100644 index 00000000000..2af11d7ecd0 --- /dev/null +++ b/python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll @@ -0,0 +1,66 @@ +/** + * Provides an extension point for for modeling sensitive data, such as secrets, certificates, or passwords. + * Sensitive data is can be interesting to use as data-flow sources in security queries. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +// Need to import since frameworks can extend `RemoteFlowSource::Range` +private import semmle.python.Frameworks +private import semmle.python.Concepts +private import semmle.python.security.SensitiveData as OldSensitiveData + +/** + * A data flow source of sensitive data, such as secrets, certificates, or passwords. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `SensitiveDataSource::Range` instead. + */ +class SensitiveDataSource extends DataFlow::Node { + SensitiveDataSource::Range range; + + SensitiveDataSource() { this = range } + + /** + * INTERNAL: Do not use. + * + * This will be rewritten to have better types soon, and therefore should only be used internally until then. + * + * Gets the classification of the sensitive data. + */ + string getClassification() { result = range.getClassification() } +} + +/** Provides a class for modeling new sources of sensitive data, such as secrets, certificates, or passwords. */ +module SensitiveDataSource { + /** + * A data flow source of sensitive data, such as secrets, certificates, or passwords. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `SensitiveDataSource` instead. + */ + abstract class Range extends DataFlow::Node { + /** + * INTERNAL: Do not use. + * + * This will be rewritten to have better types soon, and therefore should only be used internally until then. + * + * Gets the classification of the sensitive data. + */ + abstract string getClassification(); + } +} + +private class PortOfOldModeling extends SensitiveDataSource::Range { + OldSensitiveData::SensitiveData::Source oldSensitiveSource; + + PortOfOldModeling() { this.asCfgNode() = oldSensitiveSource } + + override string getClassification() { + exists(OldSensitiveData::SensitiveData classification | + oldSensitiveSource.isSourceOf(classification) + | + classification = "sensitive.data." + result + ) + } +} diff --git a/python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.expected b/python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.ql b/python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.ql new file mode 100644 index 00000000000..ce2a8a9a4fd --- /dev/null +++ b/python/ql/test/experimental/dataflow/sensitive-data/TestSensitiveDataSources.ql @@ -0,0 +1,20 @@ +import python +import semmle.python.dataflow.new.DataFlow +import TestUtilities.InlineExpectationsTest +import semmle.python.dataflow.new.SensitiveDataSources + +class SensitiveDataSourcesTest extends InlineExpectationsTest { + SensitiveDataSourcesTest() { this = "SensitiveDataSourcesTest" } + + override string getARelevantTag() { result = "SensitiveDataSource" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(SensitiveDataSource source | + location = source.getLocation() and + element = source.toString() and + value = source.getClassification() and + tag = "SensitiveDataSource" + ) + } +} diff --git a/python/ql/test/experimental/dataflow/sensitive-data/test.py b/python/ql/test/experimental/dataflow/sensitive-data/test.py new file mode 100644 index 00000000000..6241ee3afe3 --- /dev/null +++ b/python/ql/test/experimental/dataflow/sensitive-data/test.py @@ -0,0 +1,21 @@ + +from not_found import get_passwd, account_id + +def get_password(): + pass + +def get_secret(): + pass + +def fetch_certificate(): + pass + +def encrypt_password(pwd): + pass + +get_password() # $ SensitiveDataSource=password +get_passwd() # $ SensitiveDataSource=password +get_secret() # $ SensitiveDataSource=secret +fetch_certificate() # $ SensitiveDataSource=certificate +account_id() # $ SensitiveDataSource=id +safe_to_store = encrypt_password(pwd) From 499adc26a3e0c43f75c1be726995103a83606b7d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 15 Apr 2021 11:47:43 +0200 Subject: [PATCH 077/550] Python: Extend SensitiveDataSource tests Now it contains all the sort of things we actually support :+1: --- .../experimental/dataflow/sensitive-data/test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/ql/test/experimental/dataflow/sensitive-data/test.py b/python/ql/test/experimental/dataflow/sensitive-data/test.py index 6241ee3afe3..7db57fff2fa 100644 --- a/python/ql/test/experimental/dataflow/sensitive-data/test.py +++ b/python/ql/test/experimental/dataflow/sensitive-data/test.py @@ -19,3 +19,15 @@ get_secret() # $ SensitiveDataSource=secret fetch_certificate() # $ SensitiveDataSource=certificate account_id() # $ SensitiveDataSource=id safe_to_store = encrypt_password(pwd) + +# attributes +foo = ObjectFromDatabase() +foo.secret # $ SensitiveDataSource=secret +foo.username # $ SensitiveDataSource=id + +# Special handling of lookups of sensitive properties +request.args["password"], # $ MISSING: SensitiveDataSource=password +request.args.get("password") # $ SensitiveDataSource=password + +# I don't think handling `getlist` is super important, just included it to show what we don't handle +request.args.getlist("password")[0] # $ MISSING: SensitiveDataSource=password From ac83c695ade878beb40cedc4df0b38de9d58a2a0 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 11:49:38 +0200 Subject: [PATCH 078/550] Python: Add py/weak-sensitive-data-hashing query --- .../CWE-327/WeakSensitiveDataHashing.qhelp | 103 ++++++++++++ .../CWE-327/WeakSensitiveDataHashing.ql | 47 ++++++ .../examples/weak_certificate_hashing.py | 9 + .../examples/weak_password_hashing_bad.py | 4 + .../examples/weak_password_hashing_good.py | 9 + .../dataflow/WeakSensitiveDataHashing.qll | 74 +++++++++ ...WeakSensitiveDataHashingCustomizations.qll | 157 ++++++++++++++++++ .../README.md | 3 + .../WeakSensitiveDataHashing.expected | 27 +++ .../WeakSensitiveDataHashing.qlref | 1 + .../test_cryptodome.py | 25 +++ .../test_cryptography.py | 29 ++++ 12 files changed, 488 insertions(+) create mode 100644 python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp create mode 100644 python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql create mode 100644 python/ql/src/Security/CWE-327/examples/weak_certificate_hashing.py create mode 100644 python/ql/src/Security/CWE-327/examples/weak_password_hashing_bad.py create mode 100644 python/ql/src/Security/CWE-327/examples/weak_password_hashing_good.py create mode 100644 python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashing.qll create mode 100644 python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll create mode 100644 python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/README.md create mode 100644 python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected create mode 100644 python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.qlref create mode 100644 python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptodome.py create mode 100644 python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptography.py diff --git a/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp new file mode 100644 index 00000000000..05c727f0815 --- /dev/null +++ b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp @@ -0,0 +1,103 @@ + + + +

    + Using a broken or weak cryptographic hash function can leave data + vulnerable, and should not be used in security related code. +

    + +

    + A strong cryptographic hash function should be resistant to: +

    +
      +
    • + pre-image attacks: if you know a hash value h(x), + you should not be able to easily find the input x. +
    • +
    • + collision attacks: if you know a hash value h(x), + you should not be able to easily find a different input y + such that hash value is the same h(x) = h(y). +
    • +
    +

    + In cases with a limited input space, such as for passwords, the hash + function also needs to be computationally expensive to be resistant to + brute-force attacks. +

    + +

    + As an example, both MD5 and SHA-1 is known to be vulnerable to collision attacks. +

    + +

    + Since it's OK to use a weak cryptographic hash function in a non-security + context, this query only alerts when these are used to hash sensitive + data (such as passwords, certificates, usernames). +

    + +

    + Use of broken or weak cryptographic algorithms that are not hashing algorithms, is + handled by the py/weak-cryptographic-algorithm query. +

    + +
    + + +

    + Ensure that you use a strong, modern cryptographic hash function: +

    + +
      +
    • + such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space. +
    • +
    • + such as SHA-2, or SHA-3 in other cases. +
    • +
    + +
    + + +

    + The following example shows two functions for checking whether the hash + of a certificate matches a known value -- to prevent tampering. + + The first function uses MD5 that is known to be vulnerable to collision attacks. + + The second function uses SHA-256 that is a strong cryptographic hashing function. +

    + + + +
    + +

    + The following example shows two functions for hashing passwords. + + The first function uses SHA-256 to hash passwords. Although SHA-256 is a + strong cryptographic hash function, it is not suitable for password + hashing since it is not computationally expensive. +

    + + + + +

    + The second function uses Argon2 (through the argon2-cffi + PyPI package), which is a strong password hashing algorithm (and + includes a per-password salt by default). +

    + + + +
    + + +
  • OWASP: Password Storage Cheat Sheet
  • +
    + +
    diff --git a/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql new file mode 100644 index 00000000000..2df0148cce4 --- /dev/null +++ b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql @@ -0,0 +1,47 @@ +/** + * @name Use of a broken or weak cryptographic hashing algorithm on sensitive data + * @description Using broken or weak cryptographic hashing algorithms can compromise security. + * @kind path-problem + * @problem.severity warning + * @precision high + * @id py/weak-sensitive-data-hashing + * @tags security + * external/cwe/cwe-327 + * external/cwe/cwe-916 + */ + +import python +import semmle.python.security.dataflow.WeakSensitiveDataHashing +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import DataFlow::PathGraph + +from + DataFlow::PathNode source, DataFlow::PathNode sink, string ending, string algorithmName, + string classification +where + exists(NormalHashFunction::Configuration config | + config.hasFlowPath(source, sink) and + algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and + classification = source.getNode().(NormalHashFunction::Source).getClassification() and + ending = "." + ) + or + exists(ComputationallyExpensiveHashFunction::Configuration config | + config.hasFlowPath(source, sink) and + algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and + classification = + source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and + ( + sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and + ending = "." + or + not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and + ending = + " for " + classification + + " hashing, since it is not a computationally expensive hash function." + ) + ) +select sink.getNode(), source, sink, + "$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending, + source.getNode(), "Sensitive data (" + classification + ")" diff --git a/python/ql/src/Security/CWE-327/examples/weak_certificate_hashing.py b/python/ql/src/Security/CWE-327/examples/weak_certificate_hashing.py new file mode 100644 index 00000000000..8725c8b3f4f --- /dev/null +++ b/python/ql/src/Security/CWE-327/examples/weak_certificate_hashing.py @@ -0,0 +1,9 @@ +import hashlib + +def certificate_matches_known_hash_bad(certificate, known_hash): + hash = hashlib.md5(certificate).hexdigest() # BAD + return hash == known_hash + +def certificate_matches_known_hash_good(certificate, known_hash): + hash = hashlib.sha256(certificate).hexdigest() # GOOD + return hash == known_hash diff --git a/python/ql/src/Security/CWE-327/examples/weak_password_hashing_bad.py b/python/ql/src/Security/CWE-327/examples/weak_password_hashing_bad.py new file mode 100644 index 00000000000..077853131b8 --- /dev/null +++ b/python/ql/src/Security/CWE-327/examples/weak_password_hashing_bad.py @@ -0,0 +1,4 @@ +import hashlib + +def get_password_hash(password: str, salt: str): + return hashlib.sha256(password + salt).hexdigest() # BAD diff --git a/python/ql/src/Security/CWE-327/examples/weak_password_hashing_good.py b/python/ql/src/Security/CWE-327/examples/weak_password_hashing_good.py new file mode 100644 index 00000000000..e35ad31d1d7 --- /dev/null +++ b/python/ql/src/Security/CWE-327/examples/weak_password_hashing_good.py @@ -0,0 +1,9 @@ +from argon2 import PasswordHasher + +def get_initial_hash(password: str): + ph = PasswordHasher() + return ph.hash(password) # GOOD + +def check_password(password: str, known_hash): + ph = PasswordHasher() + return ph.verify(known_hash, password) # GOOD diff --git a/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashing.qll b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashing.qll new file mode 100644 index 00000000000..49634d52a4e --- /dev/null +++ b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashing.qll @@ -0,0 +1,74 @@ +/** + * Provides a taint-tracking configuration for detecting use of a broken or weak + * cryptographic hashing algorithm on sensitive data. + * + * Note, for performance reasons: only import this file if + * `WeakSensitiveDataHashing::Configuration` is needed, otherwise + * `SqlInjectionCustomizations` should be imported instead. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.dataflow.new.RemoteFlowSources +private import semmle.python.dataflow.new.BarrierGuards + +/** + * Provides a taint-tracking configuration for detecting use of a broken or weak + * cryptographic hash function on sensitive data, that does NOT require a + * computationally expensive hash function. + */ +module NormalHashFunction { + import WeakSensitiveDataHashingCustomizations::NormalHashFunction + + /** + * A taint-tracking configuration for detecting use of a broken or weak + * cryptographic hashing algorithm on sensitive data. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "NormalHashFunction" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) + or + node instanceof Sanitizer + } + } +} + +/** + * Provides a taint-tracking configuration for detecting use of a broken or weak + * cryptographic hashing algorithm on passwords. + * + * Passwords has stricter requirements on the hashing algorithm used (must be + * computationally expensive to prevent brute-force attacks). + */ +module ComputationallyExpensiveHashFunction { + import WeakSensitiveDataHashingCustomizations::ComputationallyExpensiveHashFunction + + /** + * A taint-tracking configuration for detecting use of a broken or weak + * cryptographic hashing algorithm on passwords. + * + * Passwords has stricter requirements on the hashing algorithm used (must be + * computationally expensive to prevent brute-force attacks). + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "ComputationallyExpensiveHashFunction" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) + or + node instanceof Sanitizer + } + } +} diff --git a/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll new file mode 100644 index 00000000000..e388c46d601 --- /dev/null +++ b/python/ql/src/semmle/python/security/dataflow/WeakSensitiveDataHashingCustomizations.qll @@ -0,0 +1,157 @@ +/** + * Provides default sources, sinks and sanitizers for detecting + * "use of a broken or weak cryptographic hashing algorithm on sensitive data" + * vulnerabilities, as well as extension points for adding your own. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.dataflow.new.SensitiveDataSources +private import semmle.python.dataflow.new.BarrierGuards + +/** + * Provides default sources, sinks and sanitizers for detecting + * "use of a broken or weak cryptographic hashing algorithm on sensitive data" + * vulnerabilities on sensitive data that does NOT require computationally expensive + * hashing, as well as extension points for adding your own. + * + * Also see the `ComputationallyExpensiveHashFunction` module. + */ +module NormalHashFunction { + /** + * A data flow source for "use of a broken or weak cryptographic hashing algorithm on + * sensitive data" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { + Source() { not this instanceof ComputationallyExpensiveHashFunction::Source } + + /** Gets the classification of the sensitive data. */ + abstract string getClassification(); + } + + /** + * A data flow sink for "use of a broken or weak cryptographic hashing algorithm on + * sensitive data" vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { + /** + * Gets the name of the weak hashing algorithm. + */ + abstract string getAlgorithmName(); + } + + /** + * A sanitizer for "use of a broken or weak cryptographic hashing algorithm on + * sensitive data" vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A source of sensitive data, considered as a flow source. + */ + class SensitiveDataSourceAsSource extends Source, SensitiveDataSource { + override string getClassification() { result = SensitiveDataSource.super.getClassification() } + } + + /** The input to a hashing operation using a weak algorithm, considered as a flow sink. */ + class WeakHashingOperationInputSink extends Sink { + Cryptography::HashingAlgorithm algorithm; + + WeakHashingOperationInputSink() { + exists(Cryptography::CryptographicOperation operation | + algorithm = operation.getAlgorithm() and + algorithm.isWeak() and + this = operation.getAnInput() + ) + } + + override string getAlgorithmName() { result = algorithm.getName() } + } +} + +/** + * Provides default sources, sinks and sanitizers for detecting + * "use of a broken or weak cryptographic hashing algorithm on sensitive data" + * vulnerabilities on sensitive data that DOES require computationally expensive + * hashing, as well as extension points for adding your own. + * + * Also see the `NormalHashFunction` module. + */ +module ComputationallyExpensiveHashFunction { + /** + * A data flow source of sensitive data that requires computationally expensive + * hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive + * data" vulnerabilities. + */ + abstract class Source extends DataFlow::Node { + /** Gets the classification of the sensitive data. */ + abstract string getClassification(); + } + + /** + * A data flow sink for sensitive data that requires computationally expensive + * hashing for "use of a broken or weak cryptographic hashing algorithm on sensitive + * data" vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { + /** + * Gets the name of the weak hashing algorithm. + */ + abstract string getAlgorithmName(); + + /** + * Holds if this sink is for a computationally expensive hash function (meaning that + * hash function is just weak in some other regard. + */ + abstract predicate isComputationallyExpensive(); + } + + /** + * A sanitizer of sensitive data that requires computationally expensive + * hashing for "use of a broken or weak cryptographic hashing + * algorithm on sensitive data" vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A source of passwords, considered as a flow source. + */ + class PasswordSourceAsSource extends Source, SensitiveDataSource { + PasswordSourceAsSource() { + // TODO: once https://github.com/github/codeql/pull/5739 has been merged, + // don't use hardcoded value anymore + SensitiveDataSource.super.getClassification() = "password" + } + + override string getClassification() { result = SensitiveDataSource.super.getClassification() } + } + + /** + * The input to a password hashing operation using a weak algorithm, considered as a + * flow sink. + */ + class WeakPasswordHashingOperationInputSink extends Sink { + Cryptography::CryptographicAlgorithm algorithm; + + WeakPasswordHashingOperationInputSink() { + ( + algorithm instanceof Cryptography::PasswordHashingAlgorithm and + algorithm.isWeak() + or + algorithm instanceof Cryptography::HashingAlgorithm + ) and + exists(Cryptography::CryptographicOperation operation | + algorithm = operation.getAlgorithm() and + this = operation.getAnInput() + ) + } + + override string getAlgorithmName() { result = algorithm.getName() } + + override predicate isComputationallyExpensive() { + algorithm instanceof Cryptography::PasswordHashingAlgorithm + } + } +} diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/README.md b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/README.md new file mode 100644 index 00000000000..9c6e429aefc --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/README.md @@ -0,0 +1,3 @@ +Note that the tests in this directory are very shallow, and simply show that the query is able to produce alerts. + +More in-depth tests can be found for the individual frameworks that we have modeled `Cryptography::CryptographicOperation` for. diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected new file mode 100644 index 00000000000..3a7dc65ddb4 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.expected @@ -0,0 +1,27 @@ +edges +| test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | +| test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | +| test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | +| test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | +| test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | +| test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | +nodes +| test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | semmle.label | ControlFlowNode for get_certificate() | +| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +| test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | +| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +| test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | +| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +| test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | semmle.label | ControlFlowNode for get_certificate() | +| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +| test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | +| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +| test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | +| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | semmle.label | ControlFlowNode for dangerous | +#select +| test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | test_cryptodome.py:8:19:8:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptodome.py:6:17:6:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) | +| test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | test_cryptodome.py:15:19:15:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:13:17:13:30 | ControlFlowNode for get_password() | Sensitive data (password) | +| test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | test_cryptodome.py:24:19:24:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptodome.py:20:17:20:30 | ControlFlowNode for get_password() | Sensitive data (password) | +| test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | test_cryptography.py:9:19:9:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure. | test_cryptography.py:7:17:7:33 | ControlFlowNode for get_certificate() | Sensitive data (certificate) | +| test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | test_cryptography.py:17:19:17:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:15:17:15:30 | ControlFlowNode for get_password() | Sensitive data (password) | +| test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | test_cryptography.py:27:19:27:27 | ControlFlowNode for dangerous | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | test_cryptography.py:23:17:23:30 | ControlFlowNode for get_password() | Sensitive data (password) | diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.qlref b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.qlref new file mode 100644 index 00000000000..6c8eeda7222 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/WeakSensitiveDataHashing.qlref @@ -0,0 +1 @@ +Security/CWE-327/WeakSensitiveDataHashing.ql diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptodome.py b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptodome.py new file mode 100644 index 00000000000..3e196196ef9 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptodome.py @@ -0,0 +1,25 @@ +from Cryptodome.Hash import MD5, SHA256 +from my_module import get_password, get_certificate + + +def get_badly_hashed_certificate(): + dangerous = get_certificate() + hasher = MD5.new() + hasher.update(dangerous) # NOT OK + return hasher.hexdigest() + + +def get_badly_hashed_password(): + dangerous = get_password() + hasher = MD5.new() + hasher.update(dangerous) # NOT OK + return hasher.hexdigest() + + +def get_badly_hashed_password2(): + dangerous = get_password() + # Although SHA-256 is a strong cryptographic hash functions, + # it is not suitable for password hashing. + hasher = SHA256.new() + hasher.update(dangerous) # NOT OK + return hasher.hexdigest() diff --git a/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptography.py b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptography.py new file mode 100644 index 00000000000..1090fda959c --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-327-WeakSensitiveDataHashing/test_cryptography.py @@ -0,0 +1,29 @@ +from cryptography.hazmat.primitives import hashes +from binascii import hexlify +from my_module import get_password, get_certificate + + +def get_badly_hashed_certificate(): + dangerous = get_certificate() + hasher = hashes.Hash(hashes.MD5()) + hasher.update(dangerous) # NOT OK + digest = hasher.finalize() + return hexlify(digest).decode("utf-8") + + +def get_badly_hashed_password(): + dangerous = get_password() + hasher = hashes.Hash(hashes.MD5()) + hasher.update(dangerous) # NOT OK + digest = hasher.finalize() + return hexlify(digest).decode("utf-8") + + +def get_badly_hashed_password2(): + dangerous = get_password() + # Although SHA-256 is a strong cryptographic hash functions, + # it is not suitable for password hashing. + hasher = hashes.Hash(hashes.SHA256()) + hasher.update(dangerous) # NOT OK + digest = hasher.finalize() + return hexlify(digest).decode("utf-8") From fc1a6d0e325f60868a95140d6c72e785a497230f Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 14:47:33 +0200 Subject: [PATCH 079/550] Python: Say salting is not part of py/weak-sensitive-data-hashing --- python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp index 05c727f0815..1d7b5a4f456 100644 --- a/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp +++ b/python/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp @@ -25,7 +25,8 @@

    In cases with a limited input space, such as for passwords, the hash function also needs to be computationally expensive to be resistant to - brute-force attacks. + brute-force attacks. Passwords should also have an unique salt applied + before hashing, but that is not considered by this query.

    From b82209964a74669766476c6be7619efedafa18a8 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 14 Apr 2021 13:55:01 +0200 Subject: [PATCH 080/550] Python: Add change-note for new weak crypto queries --- python/change-notes/2021-04-09-split-weak-crypto-query.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 python/change-notes/2021-04-09-split-weak-crypto-query.md diff --git a/python/change-notes/2021-04-09-split-weak-crypto-query.md b/python/change-notes/2021-04-09-split-weak-crypto-query.md new file mode 100644 index 00000000000..221b2ea07df --- /dev/null +++ b/python/change-notes/2021-04-09-split-weak-crypto-query.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Updated the _Use of a broken or weak cryptographic algorithm_ (`py/weak-cryptographic-algorithm`) query, so it alerts on any use of a weak cryptographic non-hashing algorithm. Introduced a new query _Use of a broken or weak cryptographic hashing algorithm on sensitive data_ (`py/weak-sensitive-data-hashing`) to handle weak cryptographic hashing algorithms, which only alerts when used on sensitive data. From 222c087e8c2560a58579c3f5fb1ea1405bb23532 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 15:22:08 +0200 Subject: [PATCH 081/550] Python: Remove type-tracking performance workaround Since we shouldn't need it anymore (yay) --- .../semmle/python/frameworks/Cryptography.qll | 68 ++----------------- .../src/semmle/python/frameworks/Stdlib.qll | 16 +---- 2 files changed, 5 insertions(+), 79 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Cryptography.qll b/python/ql/src/semmle/python/frameworks/Cryptography.qll index bc7bdc0109f..5ad6b9200c2 100644 --- a/python/ql/src/semmle/python/frameworks/Cryptography.qll +++ b/python/ql/src/semmle/python/frameworks/Cryptography.qll @@ -206,22 +206,7 @@ private module CryptographyModel { ] ) or - // Due to bad performance when using normal setup with `cipherInstance(t2, algorithmName).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - cipherInstance_first_join(t2, algorithmName, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate cipherInstance_first_join( - DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, - DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(cipherInstance(t2, algorithmName), res, summary) + exists(DataFlow::TypeTracker t2 | result = cipherInstance(t2, algorithmName).track(t2, t)) } /** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */ @@ -233,22 +218,7 @@ private module CryptographyModel { attr.getObject() = cipherInstance(algorithmName) ) or - // Due to bad performance when using normal setup with `cipherEncryptor(t2, algorithmName).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - cipherEncryptor_first_join(t2, algorithmName, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate cipherEncryptor_first_join( - DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, - DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(cipherEncryptor(t2, algorithmName), res, summary) + exists(DataFlow::TypeTracker t2 | result = cipherEncryptor(t2, algorithmName).track(t2, t)) } /** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */ @@ -260,22 +230,7 @@ private module CryptographyModel { attr.getObject() = cipherInstance(algorithmName) ) or - // Due to bad performance when using normal setup with `cipherDecryptor(t2, algorithmName).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - cipherDecryptor_first_join(t2, algorithmName, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate cipherDecryptor_first_join( - DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, - DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(cipherDecryptor(t2, algorithmName), res, summary) + exists(DataFlow::TypeTracker t2 | result = cipherDecryptor(t2, algorithmName).track(t2, t)) } } @@ -362,22 +317,7 @@ private module CryptographyModel { ] ) or - // Due to bad performance when using normal setup with `hashInstance(t2, algorithmName).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - hashInstance_first_join(t2, algorithmName, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate hashInstance_first_join( - DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, - DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(hashInstance(t2, algorithmName), res, summary) + exists(DataFlow::TypeTracker t2 | result = hashInstance(t2, algorithmName).track(t2, t)) } /** Gets a reference to a Hash instance using algorithm with `algorithmName`. */ diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index 5f4a2d22adb..fd7dba70898 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -886,21 +886,7 @@ private DataFlow::LocalSourceNode hashlibNewResult(DataFlow::TypeTracker t, stri t.start() and result = hashlibNewCall(algorithmName) or - // Due to bad performance when using normal setup with `hashlibNewResult(t2, algorithmName).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - hashlibNewResult_first_join(t2, algorithmName, result, summary) and - t = t2.append(summary) - ) - ) -} - -pragma[nomagic] -private predicate hashlibNewResult_first_join( - DataFlow::TypeTracker t2, string algorithmName, DataFlow::Node res, DataFlow::StepSummary summary -) { - DataFlow::StepSummary::step(hashlibNewResult(t2, algorithmName), res, summary) + exists(DataFlow::TypeTracker t2 | result = hashlibNewResult(t2, algorithmName).track(t2, t)) } /** Gets a reference to the result of calling `hashlib.new` with `algorithmName` as the first argument. */ From f9383a31bf83143b8465e5b653fc44cc4586f597 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 22 Apr 2021 15:58:28 +0200 Subject: [PATCH 082/550] Python: Fix BrokenCryptoAlgorithm.qhelp --- python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp index 5d18f6dd5d3..1b26d30e0fe 100644 --- a/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp +++ b/python/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.qhelp @@ -46,10 +46,10 @@

    NOTICE: the original - pycrypto + pycrypto PyPI package that provided the Crypto module is not longer actively maintained, so you should use the - pycryptodome + pycryptodome PyPI package instead (which has a compatible API).

    From ad12f383d95b80549ced0938a6cb858fda029766 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Apr 2021 16:54:00 +0100 Subject: [PATCH 083/550] JS: Reduce reliance on RouteHandler in Express model --- .../semmle/javascript/frameworks/Express.qll | 178 +++++++++--------- 1 file changed, 84 insertions(+), 94 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 17bfed8afda..57bca07d13b 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -390,7 +390,7 @@ module Express { } /** An Express response source. */ - abstract private class ResponseSource extends HTTP::Servers::ResponseSource { } + abstract class ResponseSource extends HTTP::Servers::ResponseSource { } /** * An Express response source, that is, the response parameter of a @@ -421,7 +421,7 @@ module Express { } /** An Express request source. */ - abstract private class RequestSource extends HTTP::Servers::RequestSource { } + abstract class RequestSource extends HTTP::Servers::RequestSource { } /** * An Express request source, that is, the request parameter of a @@ -465,73 +465,98 @@ module Express { * Gets a reference to the "query" object from a request-object originating from route-handler `rh`. */ DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { - t.startInProp("query") and - result = rh.getARequestSource() - or - exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t)) + result = queryRef(rh.getARequestSource(), t) } /** * Gets a reference to the "params" object from a request-object originating from route-handler `rh`. */ DataFlow::SourceNode getAParamsObjectReference(DataFlow::TypeTracker t, RouteHandler rh) { - t.startInProp("params") and - result = rh.getARequestSource() + result = paramsRef(rh.getARequestSource(), t) + } + + /** The input parameter to an `app.param()` route handler. */ + private class ParamHandlerInputAccess extends HTTP::RequestInputAccess { + RouteHandler rh; + + ParamHandlerInputAccess() { + exists(RouteSetup setup | rh = setup.getARouteHandler() | + this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter")) + ) + } + + override HTTP::RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = "parameter" } + } + + /** Gets a data flow node referring to `req.query`. */ + private DataFlow::SourceNode queryRef(RequestSource req, DataFlow::TypeTracker t) { + t.start() and + result = req.ref().getAPropertyRead("query") or - exists(DataFlow::TypeTracker t2 | result = getAParamsObjectReference(t2, rh).track(t2, t)) + exists(DataFlow::TypeTracker t2 | result = queryRef(req, t2).track(t2, t)) + } + + /** Gets a data flow node referring to `req.query`. */ + private DataFlow::SourceNode queryRef(RequestSource req) { + result = queryRef(req, DataFlow::TypeTracker::end()) + } + + /** Gets a data flow node referring to `req.params`. */ + private DataFlow::SourceNode paramsRef(RequestSource req, DataFlow::TypeTracker t) { + t.start() and + result = req.ref().getAPropertyRead("params") + or + exists(DataFlow::TypeTracker t2 | result = paramsRef(req, t2).track(t2, t)) + } + + /** Gets a data flow node referring to `req.params`. */ + private DataFlow::SourceNode paramsRef(RequestSource req) { + result = paramsRef(req, DataFlow::TypeTracker::end()) } /** * An access to a user-controlled Express request input. */ class RequestInputAccess extends HTTP::RequestInputAccess { - RouteHandler rh; + RequestSource request; string kind; RequestInputAccess() { kind = "parameter" and - this = - [ - getAQueryObjectReference(DataFlow::TypeTracker::end(), rh), - getAParamsObjectReference(DataFlow::TypeTracker::end(), rh) - ].getAPropertyRead() + this = [queryRef(request), paramsRef(request)].getAPropertyRead() or - exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() | + exists(DataFlow::SourceNode ref | ref = request.ref() | kind = "parameter" and - this = request.getAMethodCall("param") + this = ref.getAMethodCall("param") or // `req.originalUrl` kind = "url" and - this = request.getAPropertyRead("originalUrl") + this = ref.getAPropertyRead("originalUrl") or // `req.cookies` kind = "cookie" and - this = request.getAPropertyRead("cookies") + this = ref.getAPropertyRead("cookies") or // `req.files`, treated the same as `req.body`. // `express-fileupload` uses .files, and `multer` uses .files or .file kind = "body" and - this = request.getAPropertyRead(["files", "file"]) - ) - or - kind = "body" and - this.asExpr() = rh.getARequestBodyAccess() - or - // `value` in `router.param('foo', (req, res, next, value) => { ... })` - kind = "parameter" and - exists(RouteSetup setup | rh = setup.getARouteHandler() | - this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter")) + this = ref.getAPropertyRead(["files", "file"]) + or + kind = "body" and + this = ref.getAPropertyRead("body") ) } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = request.getRouteHandler() } override string getKind() { result = kind } override predicate isUserControlledObject() { kind = "body" and exists(ExpressLibraries::BodyParser bodyParser, RouteHandlerExpr expr | - expr.getBody() = rh and + expr.getBody() = request.getRouteHandler() and bodyParser.producesUserControlledObjects() and bodyParser.flowsToExpr(expr.getAMatchingAncestor()) ) @@ -542,13 +567,11 @@ module Express { forall(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects()) or kind = "parameter" and - exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) | - this.(DataFlow::MethodCallNode).calls(request, "param") - ) + this = request.ref().getAMethodCall("param") or // `req.query.name` kind = "parameter" and - this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead() + this = queryRef(request).getAPropertyRead() } } @@ -556,29 +579,14 @@ module Express { * An access to a header on an Express request. */ private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { - RouteHandler rh; + RequestSource request; RequestHeaderAccess() { - exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) | - exists(string methodName | - // `req.get(...)` or `req.header(...)` - this.(DataFlow::MethodCallNode).calls(request, methodName) - | - methodName = "get" or - methodName = "header" - ) - or - exists(DataFlow::PropRead headers | - // `req.headers.name` - headers.accesses(request, "headers") and - this = headers.getAPropertyRead() - ) - or - exists(string propName | propName = "host" or propName = "hostname" | - // `req.host` and `req.hostname` are derived from headers - this.(DataFlow::PropRead).accesses(request, propName) - ) - ) + this = request.ref().getAMethodCall(["get", "header"]) + or + this = request.ref().getAPropertyRead("headers").getAPropertyRead() + or + this = request.ref().getAPropertyRead(["host", "hostname"]) } override string getAHeaderName() { @@ -591,7 +599,7 @@ module Express { ) } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = request.getRouteHandler() } override string getKind() { result = "header" } } @@ -599,14 +607,7 @@ module Express { /** * HTTP headers created by Express calls */ - abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { - Expr response; - - /** - * Gets the response expression that this header is set on. - */ - Expr getResponse() { result = response } - } + abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { } /** * Holds if `e` is an HTTP request object. @@ -635,16 +636,13 @@ module Express { * An invocation of the `redirect` method of an HTTP response object. */ private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr { - RouteHandler rh; + ResponseSource response; - RedirectInvocation() { - getReceiver() = rh.getAResponseExpr() and - getMethodName() = "redirect" - } + RedirectInvocation() { this = response.ref().getAMethodCall("redirect").asExpr() } override Expr getUrlArgument() { result = getLastArgument() } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = response.getRouteHandler() } } /** @@ -662,21 +660,18 @@ module Express { * An invocation of the `set` or `header` method on an HTTP response object that * sets multiple headers. */ - class SetMultipleHeaders extends ExplicitHeader, DataFlow::ValueNode { - override MethodCallExpr astNode; - RouteHandler rh; + class SetMultipleHeaders extends ExplicitHeader, DataFlow::MethodCallNode { + ResponseSource response; SetMultipleHeaders() { - astNode.getReceiver() = rh.getAResponseExpr() and - response = astNode.getReceiver() and - astNode.getMethodName() = any(string n | n = "set" or n = "header") and - astNode.getNumArgument() = 1 + this = response.ref().getAMethodCall(["set", "header"]) and + getNumArgument() = 1 } /** * Gets a reference to the multiple headers object that is to be set. */ - private DataFlow::SourceNode getAHeaderSource() { result.flowsToExpr(astNode.getArgument(0)) } + private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(getArgument(0)) } override predicate definesExplicitly(string headerName, Expr headerValue) { exists(string header | @@ -685,7 +680,7 @@ module Express { ) } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = response.getRouteHandler() } override Expr getNameExpr() { exists(DataFlow::PropWrite write | getAHeaderSource().getAPropertyWrite() = write | @@ -705,31 +700,26 @@ module Express { * An argument passed to the `send` or `end` method of an HTTP response object. */ private class ResponseSendArgument extends HTTP::ResponseSendArgument { - RouteHandler rh; + ResponseSource response; - ResponseSendArgument() { - exists(MethodCallExpr mce | - mce.calls(rh.getAResponseExpr(), "send") and - this = mce.getArgument(0) - ) - } + ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0).asExpr() } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = response.getRouteHandler() } } /** * An invocation of the `cookie` method on an HTTP response object. */ class SetCookie extends HTTP::CookieDefinition, MethodCallExpr { - RouteHandler rh; + ResponseSource response; - SetCookie() { calls(rh.getAResponseExpr(), "cookie") } + SetCookie() { this = response.ref().getAMethodCall("cookie").asExpr() } override Expr getNameArgument() { result = getArgument(0) } override Expr getValueArgument() { result = getArgument(1) } - override RouteHandler getRouteHandler() { result = rh } + override RouteHandler getRouteHandler() { result = response.getRouteHandler() } } /** @@ -750,11 +740,11 @@ module Express { * An object passed to the `render` method of an HTTP response object. */ class TemplateObjectInput extends DataFlow::Node { - RouteHandler rh; + ResponseSource response; TemplateObjectInput() { exists(DataFlow::MethodCallNode render | - render.calls(rh.getAResponseExpr().flow(), "render") and + render = response.ref().getAMethodCall("render") and this = render.getArgument(1) ) } @@ -762,7 +752,7 @@ module Express { /** * Gets the route handler that uses this object. */ - RouteHandler getRouteHandler() { result = rh } + RouteHandler getRouteHandler() { result = response.getRouteHandler() } } /** From 822d4525af952dd75d6d0fe7c28ad2e7c034ceae Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Apr 2021 13:58:43 +0100 Subject: [PATCH 084/550] JS: Drive-by change in LogInjection --- .../semmle/javascript/security/dataflow/LogInjection.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/LogInjection.qll b/javascript/ql/src/semmle/javascript/security/dataflow/LogInjection.qll index fa70ae3f965..265b978d4a3 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/LogInjection.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/LogInjection.qll @@ -67,4 +67,11 @@ module LogInjection { class HtmlSanitizer extends Sanitizer { HtmlSanitizer() { this instanceof HtmlSanitizerCall } } + + /** + * A call to `JSON.stringify` or similar, seen as sanitizing log output. + */ + class JsonStringifySanitizer extends Sanitizer { + JsonStringifySanitizer() { this = any(JsonStringifyCall c).getOutput() } + } } From 109d1ad27fd326680cebb682f5f10a4504c10af5 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Apr 2021 14:09:58 +0100 Subject: [PATCH 085/550] JS: Model fs.promises --- javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index bb9b0870a8b..840e9ea9b78 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -465,6 +465,9 @@ module NodeJSLib { ) and t.start() or + t.start() and + result = DataFlow::moduleMember("fs", "promises") + or exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) | result = pred.track(t2, t) or From 671e968936d78437a93b2689efe6599f08d24450 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Apr 2021 16:54:11 +0100 Subject: [PATCH 086/550] JS: Model NestJS --- javascript/ql/src/javascript.qll | 2 + .../src/semmle/javascript/dataflow/Nodes.qll | 3 + .../javascript/dataflow/TaintTracking.qll | 3 +- .../javascript/frameworks/ClassValidator.qll | 67 +++ .../src/semmle/javascript/frameworks/Nest.qll | 443 ++++++++++++++++++ .../frameworks/Nest/Consistency.expected | 0 .../frameworks/Nest/Consistency.ql | 3 + .../frameworks/Nest/global/app.ts | 10 + .../frameworks/Nest/global/validation.ts | 15 + .../frameworks/Nest/local/customDecorator.ts | 26 + .../frameworks/Nest/local/customPipe.ts | 50 ++ .../frameworks/Nest/local/routes.ts | 74 +++ .../frameworks/Nest/local/validation.ts | 51 ++ .../frameworks/Nest/test.expected | 94 ++++ .../library-tests/frameworks/Nest/test.ql | 19 + .../frameworks/Nest/tsconfig.json | 6 + 16 files changed, 865 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Nest.qll create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/Consistency.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/global/app.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/test.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/test.ql create mode 100644 javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index b565a7cd21f..3818570c833 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -75,6 +75,7 @@ import semmle.javascript.frameworks.Babel import semmle.javascript.frameworks.Cheerio import semmle.javascript.frameworks.ComposedFunctions import semmle.javascript.frameworks.Classnames +import semmle.javascript.frameworks.ClassValidator import semmle.javascript.frameworks.ClientRequests import semmle.javascript.frameworks.ClosureLibrary import semmle.javascript.frameworks.CookieLibraries @@ -98,6 +99,7 @@ import semmle.javascript.frameworks.Logging import semmle.javascript.frameworks.HttpFrameworks import semmle.javascript.frameworks.HttpProxy import semmle.javascript.frameworks.Markdown +import semmle.javascript.frameworks.Nest import semmle.javascript.frameworks.Next import semmle.javascript.frameworks.NoSQL import semmle.javascript.frameworks.PkgCloud diff --git a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll index 9c8d13582c3..5a110f44ff8 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll @@ -47,6 +47,9 @@ class ParameterNode extends DataFlow::SourceNode { /** Holds if this parameter is a rest parameter. */ predicate isRestParameter() { p.isRestParameter() } + + /** Gets the data flow node for an expression that is applied to this decorator. */ + DataFlow::Node getADecorator() { result = getParameter().getADecorator().getExpression().flow() } } /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 251a691501e..1a642d980bd 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -550,7 +550,8 @@ module TaintTracking { // reading from a tainted object yields a tainted result succ.(DataFlow::PropRead).getBase() = pred and not AccessPath::DominatingPaths::hasDominatingWrite(succ) and - not isSafeClientSideUrlProperty(succ) + not isSafeClientSideUrlProperty(succ) and + not ClassValidator::isAccessToSanitizedField(succ) or // iterating over a tainted iterator taints the loop variable exists(ForOfStmt fos | diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll b/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll new file mode 100644 index 00000000000..f0e95d0583d --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll @@ -0,0 +1,67 @@ +/** + * Provides predicates for reasoning about sanitization via the `class-validator` library. + */ + +import javascript + +/** + * Provides predicates for reasoning about sanitization via the `class-validator` library. + */ +module ClassValidator { + /** + * Holds if the decorator with the given name does not sanitize the input, for the purpose of taint tracking. + */ + bindingset[name] + private predicate isSanitizingDecoratorName(string name) { + // Most decorators do sanitize the input, so only list those that don't. + not name = + [ + "IsDefined", "IsOptional", "NotEquals", "IsNotEmpty", "IsNotIn", "IsString", "IsArray", + "Contains", "NotContains", "IsAscii", "IsByteLength", "IsDataURI", "IsFQDN", "IsJSON", + "IsJWT", "IsObject", "IsNotEmptyObject", "IsLowercase", "IsSurrogatePair", "IsUrl", + "IsUppercase", "Length", "MinLength", "MaxLength", "ArrayContains", "ArrayNotContains", + "ArrayNotEmpty", "ArrayMinSize", "ArrayMaxSize", "ArrayUnique", "Allow", "ValidateNested", + "Validate", + // Consider "Matches" to be non-sanitizing as it is special-cased below + "Matches" + ] + } + + /** Holds if the given call is a decorator that sanitizes values for the purpose of taint tracking, such as `IsBoolean()`. */ + API::CallNode sanitizingDecorator() { + exists(string name | result = API::moduleImport("class-validator").getMember(name).getACall() | + isSanitizingDecoratorName(name) + or + name = "Matches" and + RegExp::isGenericRegExpSanitizer(RegExp::getRegExpFromNode(result.getArgument(0)), true) + ) + } + + /** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */ + predicate isFieldSanitizedByDecorator(FieldDefinition field) { + field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAUse() + } + + pragma[noinline] + private predicate isFieldSanitizedByDecorator(ClassDefinition cls, string name) { + isFieldSanitizedByDecorator(cls.getField(name)) + } + + pragma[noinline] + private ClassDefinition getClassReferencedByPropRead(DataFlow::PropRead read) { + read.getBase().asExpr().getType().unfold().(ClassType).getClass() = result + } + + /** + * Holds if the given property read refers to a field that has a sanitizing decorator. + * + * Only holds when TypeScript types are available. + */ + pragma[noinline] + predicate isAccessToSanitizedField(DataFlow::PropRead read) { + exists(ClassDefinition class_ | + class_ = getClassReferencedByPropRead(read) and + isFieldSanitizedByDecorator(class_, read.getPropertyName()) + ) + } +} diff --git a/javascript/ql/src/semmle/javascript/frameworks/Nest.qll b/javascript/ql/src/semmle/javascript/frameworks/Nest.qll new file mode 100644 index 00000000000..297897b3541 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/Nest.qll @@ -0,0 +1,443 @@ +/** + * Provides classes and predicates for reasoning about [Nest](https://nestjs.com/). + */ + +import javascript +private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations + +/** + * Provides classes and predicates for reasoning about [Nest](https://nestjs.com/). + */ +module NestJS { + /** Gets an API node referring to the `@nestjs/common` module. */ + private API::Node nestjs() { result = API::moduleImport("@nestjs/common") } + + /** + * Gets a data flow node that is applied as a decorator on the given function. + * + * Note that only methods in a class can have decorators. + */ + private DataFlow::Node getAFunctionDecorator(DataFlow::FunctionNode fun) { + exists(MethodDefinition method | + fun = method.getInit().flow() and + result = method.getADecorator().getExpression().flow() + ) + } + + /** + * A method that is declared as a route handler using a decorator, for example: + * + * ```js + * class C { + * @Get('posts') + * getPosts() { .. } + * } + * ``` + */ + private class NestJSRouteHandler extends HTTP::RouteHandler, DataFlow::FunctionNode { + NestJSRouteHandler() { + getAFunctionDecorator(this) = + nestjs() + .getMember(["Get", "Post", "Put", "Delete", "Patch", "Options", "Head", "All"]) + .getACall() + } + + override HTTP::HeaderDefinition getAResponseHeader(string name) { none() } + + /** + * Holds if this has the `@Redirect()` decorator. + */ + predicate hasRedirectDecorator() { + getAFunctionDecorator(this) = nestjs().getMember("Redirect").getACall() + } + + /** Gets a pipe applied to the inputs of this route handler, not including global pipes. */ + DataFlow::Node getAPipe() { + exists(DataFlow::CallNode decorator | + decorator = nestjs().getMember("UsePipes").getACall() and + result = decorator.getAnArgument() + | + decorator = getAFunctionDecorator(this) + or + exists(DataFlow::ClassNode cls | + this = cls.getAnInstanceMember() and + decorator = cls.getADecorator() + ) + ) + } + } + + /** + * A parameter with a decorator that makes it receive a value derived from the incoming request. + * + * For example, in the following, + * ```js + * @Get(':foo') + * foo(@Param('foo') foo, @Query() query) { ... } + * ``` + * the `foo` and `query` parameters receive (part of) the path and query string, respectively. + */ + private class NestJSRequestInput extends DataFlow::ParameterNode { + DataFlow::CallNode decorator; + string decoratorName; + + NestJSRequestInput() { + decoratorName = + ["Query", "Param", "Headers", "Body", "HostParam", "UploadedFile", "UploadedFiles"] and + decorator = getADecorator() and + decorator = nestjs().getMember(decoratorName).getACall() + } + + /** Gets the decorator marking this as a request input. */ + DataFlow::CallNode getDecorator() { result = decorator } + + /** Gets the route handler on which this parameter appears. */ + NestJSRouteHandler getNestRouteHandler() { result.getAParameter() = this } + + /** Gets a pipe applied to this parameter, not including global pipes. */ + DataFlow::Node getAPipe() { + result = getNestRouteHandler().getAPipe() + or + result = decorator.getArgument(1) + or + decorator.getNumArgument() = 1 and + not decorator.getArgument(0).mayHaveStringValue(_) and + result = decorator.getArgument(0) // One-argument version can either take a pipe or a property name + } + + /** Gets the kind of parameter, for use in `HTTP::RequestInputAccess`. */ + string getInputKind() { + decoratorName = ["Param", "Query"] and result = "parameter" + or + decoratorName = ["Headers", "HostParam"] and result = "header" + or + decoratorName = ["Body", "UploadedFile", "UploadedFiles"] and result = "body" + } + + /** + * Holds if this is sanitized by a sanitizing pipe, for example, a parameter + * with the decorator `@Param('x', ParseIntPipe)` is parsed as an integer, and + * is thus considered to be sanitized. + */ + predicate isSanitizedByPipe() { + hasSanitizingPipe(this, false) + or + hasSanitizingPipe(this, true) and + isSanitizingType(getParameter().getType().unfold()) + } + } + + /** API node entry point for custom implementations of `ValidationPipe` (a common pattern). */ + private class ValidationNodeEntry extends API::EntryPoint { + ValidationNodeEntry() { this = "ValidationNodeEntry" } + + override DataFlow::SourceNode getAUse() { + result.(DataFlow::ClassNode).getName() = "ValidationPipe" + } + + override DataFlow::Node getARhs() { none() } + } + + /** Gets an API node referring to the constructor of `ValidationPipe` */ + private API::Node validationPipe() { + result = nestjs().getMember("ValidationPipe") + or + result = API::root().getASuccessor(any(ValidationNodeEntry e)) + } + + /** + * Gets a pipe (instance or constructor) which causes its input to be sanitized, and thus not seen as a `RequestInputAccess`. + * + * If `dependsOnType` is `true`, then the validation depends on the declared type of the input, + * and some types may not be enough to be considered sanitized. + */ + private API::Node sanitizingPipe(boolean dependsOnType) { + exists(API::Node ctor | + dependsOnType = false and + ctor = nestjs().getMember(["ParseIntPipe", "ParseBoolPipe", "ParseUUIDPipe"]) + or + dependsOnType = true and + ctor = validationPipe() + | + result = [ctor, ctor.getInstance()] + ) + } + + /** + * Holds if `ValidationPipe` is installed as a global pipe by a file in the given folder + * or one of its enclosing folders. + * + * We use folder hierarchy to approximate the scope of globally-installed pipes. + */ + predicate hasGlobalValidationPipe(Folder folder) { + exists(DataFlow::CallNode call | + call.getCalleeName() = "useGlobalPipes" and + call.getArgument(0) = validationPipe().getInstance().getAUse() and + folder = call.getFile().getParentContainer() + ) + or + exists(API::CallNode decorator | + decorator = nestjs().getMember("Module").getACall() and + decorator + .getParameter(0) + .getMember("providers") + .getAMember() + .getMember("useFactory") + .getReturn() + .getARhs() = validationPipe().getInstance().getAUse() and + folder = decorator.getFile().getParentContainer() + ) + or + hasGlobalValidationPipe(folder.getParentContainer()) + } + + /** + * Holds if `param` is affected by a pipe that sanitizes inputs. + */ + private predicate hasSanitizingPipe(NestJSRequestInput param, boolean dependsOnType) { + param.getAPipe() = sanitizingPipe(dependsOnType).getAUse() + or + hasGlobalValidationPipe(param.getFile().getParentContainer()) and + dependsOnType = true + } + + /** + * Holds if a parameter of type `t` is considered sanitized, provided it has been checked by `ValidationPipe` + * (which relies on metadata emitted by the TypeScript compiler). + */ + private predicate isSanitizingType(Type t) { + t instanceof NumberType + or + t instanceof BooleanType + // + // Note: we could consider types with class-validator decorators to be sanitized here, but instead we consider the root + // object to be tainted, but omit taint steps for the individual properties names that have sanitizing decorators. See ClassValidator.qll. + } + + /** + * A user-defined pipe class, for example: + * ```js + * class MyPipe implements PipeTransform { + * transform(value) { return value + '!' } + * } + * ``` + * This can be used as a pipe, for example, `@Param('x', MyPipe)` would pipe + * the request parameter `x` through the `transform` function before flowing into + * the route handler. + */ + private class CustomPipeClass extends DataFlow::ClassNode { + CustomPipeClass() { + exists(ClassDefinition cls | + this = cls.flow() and + cls.getASuperInterface().hasQualifiedName("@nestjs/common", "PipeTransform") + ) + } + + DataFlow::FunctionNode getTransformFunction() { result = getInstanceMethod("transform") } + + DataFlow::ParameterNode getInputData() { result = getTransformFunction().getParameter(0) } + + DataFlow::Node getOutputData() { result = getTransformFunction().getReturnNode() } + + NestJSRequestInput getAnAffectedParameter() { + [getAnInstanceReference(), getAClassReference()].flowsTo(result.getAPipe()) + } + } + + /** + * The input to a custom pipe, seen as a remote flow source. + * + * The type of remote flow depends on which decorator is applied at the parameter, so + * we just classify it as a `RemoteFlowSource`. + */ + private class NestJSCustomPipeInput extends HTTP::RequestInputAccess { + CustomPipeClass pipe; + + NestJSCustomPipeInput() { + this = pipe.getInputData() and + exists(NestJSRequestInput input | + input = pipe.getAnAffectedParameter() and + not input.isSanitizedByPipe() + ) + } + + override string getKind() { + // Use any input kind that the pipe is applied to. + result = pipe.getAnAffectedParameter().getInputKind() + } + + override HTTP::RouteHandler getRouteHandler() { + result = pipe.getAnAffectedParameter().getNestRouteHandler() + } + } + + /** + * A step from the result of a custom pipe, to an affected parameter. + */ + private class CustomPipeStep extends DataFlow::SharedFlowStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(CustomPipeClass pipe | + pred = pipe.getOutputData() and + succ = pipe.getAnAffectedParameter() + ) + } + } + + /** + * A request input parameter that is not sanitized by a pipe, and therefore treated + * as a source of untrusted data. + */ + private class NestJSRequestInputAsRequestInputAccess extends NestJSRequestInput, + HTTP::RequestInputAccess { + NestJSRequestInputAsRequestInputAccess() { + not isSanitizedByPipe() and + not this = any(CustomPipeClass cls).getAnAffectedParameter() + } + + override HTTP::RouteHandler getRouteHandler() { result = getNestRouteHandler() } + + override string getKind() { result = getInputKind() } + + override predicate isUserControlledObject() { + not exists(getAPipe()) and // value is not transformed by a pipe + ( + decorator.getNumArgument() = 0 + or + decoratorName = ["Query", "Body"] + ) + } + } + + private class NestJSHeaderAccess extends NestJSRequestInputAsRequestInputAccess, + HTTP::RequestHeaderAccess { + NestJSHeaderAccess() { decoratorName = "Headers" and decorator.getNumArgument() > 0 } + + override string getAHeaderName() { + result = decorator.getArgument(0).getStringValue().toLowerCase() + } + } + + /** + * A return value from a route handler, seen as an argument to `res.send()`. + * + * For example, + * ```js + * @Get() + * foo() { + * return 'Hello'; + * } + * ``` + * writes `Hello` to the response. + */ + private class ReturnValueAsResponseSend extends HTTP::ResponseSendArgument { + NestJSRouteHandler handler; + + ReturnValueAsResponseSend() { + not handler.hasRedirectDecorator() and + this = handler.getAReturn().asExpr() + } + + override HTTP::RouteHandler getRouteHandler() { result = handler } + } + + /** + * A return value from a redirecting route handler, seen as a sink for server-side redirect. + * + * For example, + * ```js + * @Get() + * @Redirect + * foo() { + * return { url: 'https://example.com' } + * } + * ``` + * redirects to `https://example.com`. + */ + private class ReturnValueAsRedirection extends ServerSideUrlRedirect::Sink { + NestJSRouteHandler handler; + + ReturnValueAsRedirection() { + handler.hasRedirectDecorator() and + this = handler.getAReturn().getALocalSource().getAPropertyWrite("url").getRhs() + } + } + + /** + * A parameter decorator created using `createParamDecorator`. + */ + private class CustomParameterDecorator extends API::CallNode { + CustomParameterDecorator() { this = nestjs().getMember("createParamDecorator").getACall() } + + /** Gets the `context` parameter. */ + API::Node getExecutionContext() { result = getParameter(0).getParameter(1) } + + /** Gets a parameter with this decorator applied. */ + DataFlow::ParameterNode getADecoratedParameter() { + result.getADecorator() = getReturn().getReturn().getAUse() + } + + /** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */ + DataFlow::Node getResult() { result = getParameter(0).getReturn().getARhs() } + } + + /** + * A flow step from a custom parameter decorator to a decorated parameter. + */ + private class CustomParameterFlowStep extends DataFlow::SharedFlowStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(CustomParameterDecorator dec | + pred = dec.getResult() and + succ = dec.getADecoratedParameter() + ) + } + } + + private API::Node executionContext() { + result = API::Node::ofType("@nestjs/common", "ExecutionContext") + or + result = any(CustomParameterDecorator d).getExecutionContext() + } + + /** + * A source of `express` request objects, based on the `@Req()` decorator, + * or the context object in a custom decorator. + */ + private class ExpressRequestSource extends Express::RequestSource { + ExpressRequestSource() { + this.(DataFlow::ParameterNode).getADecorator() = + nestjs().getMember(["Req", "Request"]).getReturn().getAnImmediateUse() + or + this = + executionContext() + .getMember("switchToHttp") + .getReturn() + .getMember("getRequest") + .getReturn() + .getAnImmediateUse() + } + + /** + * Gets the route handler that handles this request. + */ + override HTTP::RouteHandler getRouteHandler() { + result.(DataFlow::FunctionNode).getAParameter() = this + } + } + + /** + * A source of `express` response objects, based on the `@Res()` decorator. + */ + private class ExpressResponseSource extends Express::ResponseSource { + ExpressResponseSource() { + this.(DataFlow::ParameterNode).getADecorator() = + nestjs().getMember(["Res", "Response"]).getReturn().getAnImmediateUse() + } + + /** + * Gets the route handler that handles this request. + */ + override HTTP::RouteHandler getRouteHandler() { + result.(DataFlow::FunctionNode).getAParameter() = this + } + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/Consistency.expected b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql new file mode 100644 index 00000000000..787d0a5fdc4 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql @@ -0,0 +1,3 @@ +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.ReflectedXss +import semmle.javascript.security.dataflow.ServerSideUrlRedirect diff --git a/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts b/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts new file mode 100644 index 00000000000..132f2162a9f --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts @@ -0,0 +1,10 @@ +import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.useGlobalPipes(new ValidationPipe()); + await app.listen(3000); +} +bootstrap(); diff --git a/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts b/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts new file mode 100644 index 00000000000..4bf67eff4cb --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts @@ -0,0 +1,15 @@ +import { Get, Query } from '@nestjs/common'; +import { IsIn } from 'class-validator'; + +export class Controller { + @Get() + route1(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return unvalidated; // NOT OK + return validatedObj.key; // OK + } +} + +class Struct { + @IsIn(['foo', 'bar']) + key: string; +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts new file mode 100644 index 00000000000..032d7032bc0 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts @@ -0,0 +1,26 @@ +import { Get, createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const SneakyQueryParam = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + return request.query.sneakyQueryParam; + }, +); + +export const SafeParam = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + return 'Safe'; + }, +); + +export class Controller { + @Get() + sneaky(@SneakyQueryParam() value) { + return value; // NOT OK + } + + @Get() + safe(@SafeParam() value) { + return value; // OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts new file mode 100644 index 00000000000..58f6084ab0b --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts @@ -0,0 +1,50 @@ +import { Get, Injectable, PipeTransform, Query, UsePipes } from '@nestjs/common'; + +@Injectable() +export class CustomSanitizingPipe implements PipeTransform { + transform(value: string): number | undefined { + if (value == null) return undefined; + return Number(value); + } +} + +@Injectable() +export class CustomPropagatingPipe implements PipeTransform { + transform(value: string): string { + return value.toUpperCase() + '!'; + } +} + +export class Controller { + @Get() + sanitizingPipe1(@Query('x', CustomSanitizingPipe) sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + sanitizingPipe2(@Query('x', new CustomSanitizingPipe()) sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + @UsePipes(CustomSanitizingPipe) + sanitizingPipe3(@Query('x') sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + propagatingPipe1(@Query('x', CustomPropagatingPipe) unsanitized: string): string { + return '' + unsanitized; // NOT OK + } + + @Get() + propagatingPipe2(@Query('x', new CustomPropagatingPipe()) unsanitized: string): string { + return '' + unsanitized; // NOT OK + } + + @Get() + @UsePipes(CustomPropagatingPipe) + propagatingPipe3(@Query('x') unsanitized: string): string { + return '' + unsanitized; // NOT OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts new file mode 100644 index 00000000000..b94c3942318 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts @@ -0,0 +1,74 @@ +import { Get, Post, All, Query, Param, Body, Redirect, Req, Res, UploadedFile, UploadedFiles } from '@nestjs/common'; +import { SneakyQueryParam } from './customDecorator'; + +export class TestController { + @Get('foo') + getFoo() { + return 'foo'; + } + + @Post('foo') + postFoo() { + return 'foo'; + } + + @Get() + getRoot() { + return 'foo'; + } + + @All('bar') + bar() { + return 'bar'; + } + + @Get('requestInputs/:x') + requestInputs( + @Param('x') x, + @Query() queryObj, + @Query('name') name, + @Req() req + ) { + if (Math.random()) return x; // NOT OK + if (Math.random()) return queryObj; // NOT OK + if (Math.random()) return name; // NOT OK + if (Math.random()) return req.query.abc; // NOT OK + return; + } + + @Post('post') + post(@Body() body) { + return body.x; // NOT OK + } + + @Get('redir') + @Redirect('https://example.com') + redir() { + return { + url: '//other.example.com' // OK + }; + } + + @Get('redir') + @Redirect('https://example.com') + redir2(@Query('redirect') target) { + return { + url: target // NOT OK + }; + } + + @Get() + explicitSend(@Req() req, @Res() res) { + res.send(req.query.x) // NOT OK + } + + @Post() + upload(@UploadedFile() file) { + return file.originalname; // NOT OK + } + + @Post() + uploadMany(@UploadedFiles() files) { + return files[0].originalname; // NOT OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts new file mode 100644 index 00000000000..d9771c195e1 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts @@ -0,0 +1,51 @@ +import { Get, Query, UsePipes, ValidationPipe } from '@nestjs/common'; +import { IsIn } from 'class-validator'; + +export class Controller { + @Get() + route1(@Query('x', new ValidationPipe()) validatedObj: Struct) { + return validatedObj.key; // OK + } + + @Get() + route2(@Query('x', ValidationPipe) validatedObj: Struct) { + return validatedObj.key; // OK + } + + @Get() + @UsePipes(new ValidationPipe()) + route3(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } + + @Get() + @UsePipes(ValidationPipe) + route4(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +@UsePipes(new ValidationPipe()) +export class Controller2 { + @Get() + route5(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +@UsePipes(ValidationPipe) +export class Controller3 { + @Get() + route6(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +class Struct { + @IsIn(['foo', 'bar']) + key: string; +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.expected b/javascript/ql/test/library-tests/frameworks/Nest/test.expected new file mode 100644 index 00000000000..9de016189ba --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.expected @@ -0,0 +1,94 @@ +routeHandler +| global/validation.ts:6:3:9:3 | route1( ... OK\\n } | +| local/customDecorator.ts:18:3:20:3 | sneaky( ... OK\\n } | +| local/customDecorator.ts:23:3:25:3 | safe(@S ... OK\\n } | +| local/customPipe.ts:20:5:22:5 | sanitiz ... K\\n } | +| local/customPipe.ts:25:5:27:5 | sanitiz ... K\\n } | +| local/customPipe.ts:31:5:33:5 | sanitiz ... K\\n } | +| local/customPipe.ts:36:5:38:5 | propaga ... K\\n } | +| local/customPipe.ts:41:5:43:5 | propaga ... K\\n } | +| local/customPipe.ts:47:5:49:5 | propaga ... K\\n } | +| local/routes.ts:6:3:8:3 | getFoo( ... o';\\n } | +| local/routes.ts:11:3:13:3 | postFoo ... o';\\n } | +| local/routes.ts:16:3:18:3 | getRoot ... o';\\n } | +| local/routes.ts:21:3:23:3 | bar() { ... r';\\n } | +| local/routes.ts:26:3:37:3 | request ... rn;\\n } | +| local/routes.ts:40:3:42:3 | post(@B ... OK\\n } | +| local/routes.ts:46:3:50:3 | redir() ... };\\n } | +| local/routes.ts:54:3:58:3 | redir2( ... };\\n } | +| local/routes.ts:61:3:63:3 | explici ... OK\\n } | +| local/routes.ts:66:3:68:3 | upload( ... OK\\n } | +| local/routes.ts:71:3:73:3 | uploadM ... OK\\n } | +| local/validation.ts:6:3:8:3 | route1( ... OK\\n } | +| local/validation.ts:11:3:13:3 | route2( ... OK\\n } | +| local/validation.ts:17:3:20:3 | route3( ... OK\\n } | +| local/validation.ts:24:3:27:3 | route4( ... OK\\n } | +| local/validation.ts:33:3:36:3 | route5( ... OK\\n } | +| local/validation.ts:42:3:45:3 | route6( ... OK\\n } | +requestSource +| local/customDecorator.ts:5:21:5:51 | ctx.swi ... quest() | +| local/routes.ts:30:12:30:14 | req | +| local/routes.ts:61:23:61:25 | req | +responseSource +| local/routes.ts:61:35:61:37 | res | +requestInputAccess +| body | local/routes.ts:40:16:40:19 | body | +| body | local/routes.ts:66:26:66:29 | file | +| body | local/routes.ts:71:31:71:35 | files | +| parameter | global/validation.ts:6:22:6:33 | validatedObj | +| parameter | global/validation.ts:6:56:6:66 | unvalidated | +| parameter | local/customDecorator.ts:6:12:6:41 | request ... ryParam | +| parameter | local/customPipe.ts:5:15:5:19 | value | +| parameter | local/customPipe.ts:13:15:13:19 | value | +| parameter | local/routes.ts:27:17:27:17 | x | +| parameter | local/routes.ts:28:14:28:21 | queryObj | +| parameter | local/routes.ts:29:20:29:23 | name | +| parameter | local/routes.ts:35:31:35:43 | req.query.abc | +| parameter | local/routes.ts:54:29:54:34 | target | +| parameter | local/routes.ts:62:14:62:24 | req.query.x | +| parameter | local/validation.ts:6:44:6:55 | validatedObj | +| parameter | local/validation.ts:11:38:11:49 | validatedObj | +| parameter | local/validation.ts:17:22:17:33 | validatedObj | +| parameter | local/validation.ts:17:56:17:66 | unvalidated | +| parameter | local/validation.ts:24:22:24:33 | validatedObj | +| parameter | local/validation.ts:24:56:24:66 | unvalidated | +| parameter | local/validation.ts:33:22:33:33 | validatedObj | +| parameter | local/validation.ts:33:56:33:66 | unvalidated | +| parameter | local/validation.ts:42:22:42:33 | validatedObj | +| parameter | local/validation.ts:42:56:42:66 | unvalidated | +responseSendArgument +| global/validation.ts:7:31:7:41 | unvalidated | +| global/validation.ts:8:12:8:27 | validatedObj.key | +| local/customDecorator.ts:19:12:19:16 | value | +| local/customDecorator.ts:24:12:24:16 | value | +| local/customPipe.ts:21:16:21:29 | '' + sanitized | +| local/customPipe.ts:26:16:26:29 | '' + sanitized | +| local/customPipe.ts:32:16:32:29 | '' + sanitized | +| local/customPipe.ts:37:16:37:31 | '' + unsanitized | +| local/customPipe.ts:42:16:42:31 | '' + unsanitized | +| local/customPipe.ts:48:16:48:31 | '' + unsanitized | +| local/routes.ts:7:12:7:16 | 'foo' | +| local/routes.ts:12:12:12:16 | 'foo' | +| local/routes.ts:17:12:17:16 | 'foo' | +| local/routes.ts:22:12:22:16 | 'bar' | +| local/routes.ts:32:31:32:31 | x | +| local/routes.ts:33:31:33:38 | queryObj | +| local/routes.ts:34:31:34:34 | name | +| local/routes.ts:35:31:35:43 | req.query.abc | +| local/routes.ts:41:12:41:17 | body.x | +| local/routes.ts:62:14:62:24 | req.query.x | +| local/routes.ts:67:12:67:28 | file.originalname | +| local/routes.ts:72:12:72:32 | files[0 ... nalname | +| local/validation.ts:7:12:7:27 | validatedObj.key | +| local/validation.ts:12:12:12:27 | validatedObj.key | +| local/validation.ts:18:31:18:46 | validatedObj.key | +| local/validation.ts:19:12:19:22 | unvalidated | +| local/validation.ts:25:31:25:46 | validatedObj.key | +| local/validation.ts:26:12:26:22 | unvalidated | +| local/validation.ts:34:31:34:46 | validatedObj.key | +| local/validation.ts:35:12:35:22 | unvalidated | +| local/validation.ts:43:31:43:46 | validatedObj.key | +| local/validation.ts:44:12:44:22 | unvalidated | +redirectSink +| local/routes.ts:48:12:48:32 | '//othe ... le.com' | +| local/routes.ts:56:12:56:17 | target | diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.ql b/javascript/ql/test/library-tests/frameworks/Nest/test.ql new file mode 100644 index 00000000000..120727d2548 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.ql @@ -0,0 +1,19 @@ +import javascript +private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations + +query HTTP::RouteHandler routeHandler() { any() } + +query HTTP::Servers::RequestSource requestSource() { any() } + +query HTTP::Servers::ResponseSource responseSource() { any() } + +query RemoteFlowSource requestInputAccess(string kind) { + kind = result.(HTTP::RequestInputAccess).getKind() + or + not result instanceof HTTP::RequestInputAccess and + kind = "RemoteFlowSource" +} + +query HTTP::ResponseSendArgument responseSendArgument() { any() } + +query ServerSideUrlRedirect::Sink redirectSink() { any() } diff --git a/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json b/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json new file mode 100644 index 00000000000..c5df21cd7d4 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "experimentalDecorators": true + }, + "include": ["."] +} From d0b8b323459760231ede07df240acc29aed87fbb Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Apr 2021 14:33:05 +0100 Subject: [PATCH 087/550] JS: Add change notes --- javascript/change-notes/2021-04-15-fs-promises.md | 3 +++ javascript/change-notes/2021-04-15-nestjs.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 javascript/change-notes/2021-04-15-fs-promises.md create mode 100644 javascript/change-notes/2021-04-15-nestjs.md diff --git a/javascript/change-notes/2021-04-15-fs-promises.md b/javascript/change-notes/2021-04-15-fs-promises.md new file mode 100644 index 00000000000..f410f00edb1 --- /dev/null +++ b/javascript/change-notes/2021-04-15-fs-promises.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Support for `fs.promises` has been added, leading to more results for security queries + related to file system access. diff --git a/javascript/change-notes/2021-04-15-nestjs.md b/javascript/change-notes/2021-04-15-nestjs.md new file mode 100644 index 00000000000..5eaa0e5af69 --- /dev/null +++ b/javascript/change-notes/2021-04-15-nestjs.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Support for Nest.js has been added. The security queries now recognize sources and sinks + specific to the Nest.js framework. From 4f53a1ab409bce25bc0ac52ef60582f2d884f83f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 21 Apr 2021 14:48:52 +0100 Subject: [PATCH 088/550] JS: Cache ClassNode::Range --- .../ql/src/semmle/javascript/dataflow/Nodes.qll | 11 +++++++++++ .../src/semmle/javascript/internal/CachedStages.qll | 2 ++ 2 files changed, 13 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll index 5a110f44ff8..19fe93da259 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll @@ -1081,35 +1081,42 @@ module ClassNode { * Subclass this to introduce new kinds of class nodes. If you want to refine * the definition of existing class nodes, subclass `DataFlow::ClassNode` instead. */ + cached abstract class Range extends DataFlow::SourceNode { /** * Gets the name of the class, if it has one. */ + cached abstract string getName(); /** * Gets a description of the class. */ + cached abstract string describe(); /** * Gets the constructor function of this class. */ + cached abstract FunctionNode getConstructor(); /** * Gets the instance member with the given name and kind. */ + cached abstract FunctionNode getInstanceMember(string name, MemberKind kind); /** * Gets an instance member with the given kind. */ + cached abstract FunctionNode getAnInstanceMember(MemberKind kind); /** * Gets the static method of this class with the given name. */ + cached abstract FunctionNode getStaticMethod(string name); /** @@ -1117,20 +1124,24 @@ module ClassNode { * * The constructor is not considered a static method. */ + cached abstract FunctionNode getAStaticMethod(); /** * Gets a dataflow node representing a class to be used as the super-class * of this node. */ + cached abstract DataFlow::Node getASuperClassNode(); /** * Gets the type annotation for the field `fieldName`, if any. */ + cached TypeAnnotation getFieldTypeAnnotation(string fieldName) { none() } /** Gets a decorator applied to this class. */ + cached DataFlow::Node getADecorator() { none() } } diff --git a/javascript/ql/src/semmle/javascript/internal/CachedStages.qll b/javascript/ql/src/semmle/javascript/internal/CachedStages.qll index 6980a6eef7f..80d0930cbed 100644 --- a/javascript/ql/src/semmle/javascript/internal/CachedStages.qll +++ b/javascript/ql/src/semmle/javascript/internal/CachedStages.qll @@ -135,6 +135,8 @@ module Stages { exists(any(AccessPath a).getAnInstanceIn(_)) or exists(any(DataFlow::PropRef ref).getBase()) + or + exists(any(DataFlow::ClassNode cls)) } } From 71e3041370735b3220853a666722b06a13b58df7 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 23 Apr 2021 13:15:13 +0100 Subject: [PATCH 089/550] JS: Fewer spurious reflected xss sinks --- .../src/semmle/javascript/frameworks/Nest.qll | 26 +++++++++++++++++-- .../frameworks/Nest/test.expected | 4 --- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Nest.qll b/javascript/ql/src/semmle/javascript/frameworks/Nest.qll index 297897b3541..d216fac1712 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Nest.qll @@ -51,6 +51,15 @@ module NestJS { getAFunctionDecorator(this) = nestjs().getMember("Redirect").getACall() } + /** + * Holds if the return value is sent back in the response. + */ + predicate isReturnValueReflected() { + getAFunctionDecorator(this) = nestjs().getMember(["Get", "Post"]).getACall() and + not hasRedirectDecorator() and + not getAFunctionDecorator(this) = nestjs().getMember("Render").getACall() + } + /** Gets a pipe applied to the inputs of this route handler, not including global pipes. */ DataFlow::Node getAPipe() { exists(DataFlow::CallNode decorator | @@ -317,6 +326,14 @@ module NestJS { } } + private predicate isStringType(Type type) { + type instanceof StringType + or + type instanceof AnyType + or + isStringType(type.(PromiseType).getElementType().unfold()) + } + /** * A return value from a route handler, seen as an argument to `res.send()`. * @@ -333,8 +350,13 @@ module NestJS { NestJSRouteHandler handler; ReturnValueAsResponseSend() { - not handler.hasRedirectDecorator() and - this = handler.getAReturn().asExpr() + handler.isReturnValueReflected() and + this = handler.getAReturn().asExpr() and + // Only returned strings are sinks + not exists(Type type | + type = getType() and + not isStringType(type.unfold()) + ) } override HTTP::RouteHandler getRouteHandler() { result = handler } diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.expected b/javascript/ql/test/library-tests/frameworks/Nest/test.expected index 9de016189ba..c659295f552 100644 --- a/javascript/ql/test/library-tests/frameworks/Nest/test.expected +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.expected @@ -67,10 +67,6 @@ responseSendArgument | local/customPipe.ts:37:16:37:31 | '' + unsanitized | | local/customPipe.ts:42:16:42:31 | '' + unsanitized | | local/customPipe.ts:48:16:48:31 | '' + unsanitized | -| local/routes.ts:7:12:7:16 | 'foo' | -| local/routes.ts:12:12:12:16 | 'foo' | -| local/routes.ts:17:12:17:16 | 'foo' | -| local/routes.ts:22:12:22:16 | 'bar' | | local/routes.ts:32:31:32:31 | x | | local/routes.ts:33:31:33:38 | queryObj | | local/routes.ts:34:31:34:34 | name | From 0da0670a79750f40f03c3fbbad78210af655b342 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 23 Apr 2021 11:04:19 +0100 Subject: [PATCH 090/550] JS: Add Nest.js to list of supported framworks --- docs/codeql/support/reusables/frameworks.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 22569cbdcc6..5f88e3c907d 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -128,6 +128,7 @@ JavaScript and TypeScript built-in support mssql, Database mysql, Database node, Runtime environment + nest.js, Server postgres, Database ramda, Utility library react, HTML framework From f2b2300da90f2ff9680f8934985784b968ed7642 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:23:31 +0300 Subject: [PATCH 091/550] Add files via upload --- .../Security/CWE/CWE-415/DoubleFree.c | 13 +++++ .../Security/CWE/CWE-415/DoubleFree.qhelp | 26 ++++++++++ .../Security/CWE/CWE-415/DoubleFree.ql | 51 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c new file mode 100644 index 00000000000..978ca445215 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c @@ -0,0 +1,13 @@ +... + buf = malloc(intSize); +... + free(buf); + buf = NULL; // GOOD +... + +... + buf = malloc(intSize); +... + free(buf); + if(buf) free(buf); // BAD: the cleanup function does not zero out the pointer +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp new file mode 100644 index 00000000000..f30d2f2c48b --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp @@ -0,0 +1,26 @@ + + + +

    Double freeing of a previously allocated resource can lead to various vulnerabilities in the program. Requires the attention of developers.

    + +
    + +

    We recommend that you exclude situations of possible double release.

    + +
    + +

    The following example demonstrates an erroneous and corrected use of freeing a pointer.

    + + +
    + + +
  • + CERT C Coding Standard: + MEM30-C. Do not access freed memory. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql new file mode 100644 index 00000000000..199dc18bee7 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql @@ -0,0 +1,51 @@ +/** + * @name Errors When Double Free + * @description Double freeing of a previously allocated resource can lead to various vulnerabilities in the program + * and requires the attention of the developer. + * @kind problem + * @id cpp/errors-when-double-free + * @problem.severity warning + * @precision medium + * @tags security + * external/cwe/cwe-415 + */ + +import cpp + +/** + * The function allows `getASuccessor` to be called recursively. + * This provides a stop in situations of possible influence on the pointer. + */ +ControlFlowNode recursASuccessor(FunctionCall fc, LocalScopeVariable v) { + result = fc + or + exists(ControlFlowNode mid | + mid = recursASuccessor(fc, v) and + result = mid.getASuccessor() and + not result = v.getAnAssignedValue() and + not result.(AddressOfExpr).getOperand() = v.getAnAccess() and + not ( + not result instanceof DeallocationExpr and + result.(FunctionCall).getAnArgument().(VariableAccess).getTarget() = v + ) and + ( + fc.getTarget().hasGlobalOrStdName("realloc") and + ( + not fc.getParent*() instanceof IfStmt and + not result instanceof IfStmt + ) + or + not fc.getTarget().hasGlobalOrStdName("realloc") + ) + ) +} + +from FunctionCall fc +where + exists(FunctionCall fc2, LocalScopeVariable v | + freeCall(fc, v.getAnAccess()) and + freeCall(fc2, v.getAnAccess()) and + fc != fc2 and + recursASuccessor(fc, v) = fc2 + ) +select fc.getArgument(0), "This pointer may be cleared again later." From c1d125b3786d5d0afa5071a9165e5ba7235bc457 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:25:17 +0300 Subject: [PATCH 092/550] Add files via upload --- .../CWE-415/semmle/tests/DoubleFree.expected | 2 ++ .../CWE/CWE-415/semmle/tests/DoubleFree.qlref | 1 + .../Security/CWE/CWE-415/semmle/tests/test.c | 21 +++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected new file mode 100644 index 00000000000..669c65cba2a --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected @@ -0,0 +1,2 @@ +| test.c:9:8:9:10 | buf | This pointer may be cleared again later. | +| test.c:19:26:19:29 | buf1 | This pointer may be cleared again later. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref new file mode 100644 index 00000000000..242beb593f8 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-415/DoubleFree.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c new file mode 100644 index 00000000000..1ebf3546480 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c @@ -0,0 +1,21 @@ +typedef unsigned long size_t; +void *malloc(size_t size); +void free(void *ptr); + +void workFunction_0(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // BAD + if(buf) free(buf); +} +void workFunction_1(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + buf1 = (char *) realloc(buf,intSize*2); + if(buf) free(buf); // GOOD + buf = (char *) realloc(buf1,intSize*4); // BAD + free(buf1); +} From 50c63a88c3036b7ffddd579fe5454fe3ad72cf2f Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:34:41 +0300 Subject: [PATCH 093/550] Add files via upload --- ...tionOfVariableWithUnnecessarilyWideScope.c | 14 +++++ ...OfVariableWithUnnecessarilyWideScope.qhelp | 26 ++++++++ ...ionOfVariableWithUnnecessarilyWideScope.ql | 62 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c new file mode 100644 index 00000000000..b09971b5328 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -0,0 +1,14 @@ +while(intIndex > 2) +{ + ... + intIndex--; + ... +} // GOOD: coreten cycle +... +while(intIndex > 2) +{ + ... + int intIndex; + intIndex--; + ... +} // BAD: the variable used in the condition does not change. diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp new file mode 100644 index 00000000000..d84f47f5453 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp @@ -0,0 +1,26 @@ + + + +

    Using variables with the same name is dangerous. However, such a situation inside the while loop can lead to a violation of the accessibility of the program. Requires the attention of developers.

    + +
    + +

    We recommend not to use local variables inside a loop if their names are the same as the variables in the condition of this loop.

    + +
    + +

    The following example demonstrates an erroneous and corrected use of a local variable within a loop.

    + + +
    + + +
  • + CERT C Coding Standard: + DCL01-C. Do not reuse variable names in subscopes. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql new file mode 100644 index 00000000000..a253a5e0599 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -0,0 +1,62 @@ +/** + * @name Errors When Using Variable Declaration Inside Loop + * @description Using variables with the same name is dangerous. + * However, such a situation inside the while loop can lead to a violation of the accessibility of the program. + * Requires the attention of developers. + * @kind problem + * @id cpp/errors-when-using-variable-declaration-inside-loop + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-1126 + */ + +import cpp + +/** + * Errors when using a variable declaration inside a loop. + */ +class DangerousWhileLoop extends WhileStmt { + Expr exp; + Declaration dl; + + DangerousWhileLoop() { + this = dl.getParentScope().(BlockStmt).getParent*() and + exp = this.getCondition().getAChild*() and + not exp instanceof PointerFieldAccess and + not exp instanceof ValueFieldAccess and + exp.toString() = dl.getName() and + not exp.getParent*() instanceof CrementOperation and + not exp.getParent*() instanceof Assignment and + not exp.getParent*() instanceof FunctionCall + } + + Declaration getDeclaration() { result = dl } + + /** Holds when there are changes to the variables involved in the condition. */ + predicate isUseThisVariable() { + exists(Variable v | + this.getCondition().getAChild*().(VariableAccess).getTarget() = v and + ( + exists(Assignment aexp | + aexp = this.getStmt().getAChild*() and + ( + aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v + or + aexp.getLValue().(VariableAccess).getTarget() = v + ) + ) + or + exists(CrementOperation crm | + crm = this.getStmt().getAChild*() and + crm.getOperand().(VariableAccess).getTarget() = v + ) + ) + ) + } +} + +from DangerousWhileLoop lp +where not lp.isUseThisVariable() +select lp.getDeclaration(), "A variable with this name is used in the loop condition." From 98f7f70814efa3dae55beb0fd1cb9f4e095d776e Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 25 Apr 2021 22:35:40 +0300 Subject: [PATCH 094/550] Add files via upload --- ...nOfVariableWithUnnecessarilyWideScope.expected | 1 + ...tionOfVariableWithUnnecessarilyWideScope.qlref | 1 + .../Security/CWE/CWE-1126/semmle/tests/test.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected new file mode 100644 index 00000000000..7b540a33384 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -0,0 +1 @@ +| test.c:12:9:12:16 | intIndex | A variable with this name is used in the loop condition. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref new file mode 100644 index 00000000000..6da5822f7f0 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c new file mode 100644 index 00000000000..090bed34d45 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -0,0 +1,15 @@ +void workFunction_0(char *s) { + int intIndex = 10; + char buf[80]; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex--; + } + while(intIndex > 2) + { + buf[intIndex] = 1; + int intIndex; // BAD + intIndex--; + } +} From 3d891f0b391f3623c1baf5fc1ced7b8982e259b2 Mon Sep 17 00:00:00 2001 From: p0wn4j Date: Wed, 24 Mar 2021 00:08:39 +0400 Subject: [PATCH 095/550] [Java] CWE-078: Add JSch OS command injection sink --- .../Security/CWE/CWE-078/ExecCommon.qll | 32 ++++++++++ .../Security/CWE/CWE-078/ExecTainted.ql | 24 +++++++ .../CWE/CWE-078/JSchOSInjection.qhelp | 64 +++++++++++++++++++ .../Security/CWE/CWE-078/JSchOSInjection.qll | 20 ++++++ .../CWE/CWE-078/JSchOSInjectionBad.java | 17 +++++ .../CWE/CWE-078/JSchOSInjectionSanitized.java | 46 +++++++++++++ .../security/CWE-078/ExecTainted.expected | 11 ++++ .../security/CWE-078/ExecTainted.qlref | 1 + .../security/CWE-078/JSchOSInjectionTest.java | 56 ++++++++++++++++ .../query-tests/security/CWE-078/options | 2 + .../jsch-0.1.55/com/jcraft/jsch/Channel.java | 46 +++++++++++++ .../com/jcraft/jsch/ChannelExec.java | 40 ++++++++++++ .../com/jcraft/jsch/ChannelSession.java | 33 ++++++++++ .../jsch-0.1.55/com/jcraft/jsch/JSch.java | 40 ++++++++++++ .../jsch-0.1.55/com/jcraft/jsch/Session.java | 55 ++++++++++++++++ 15 files changed, 487 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-078/JSchOSInjectionTest.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-078/options create mode 100644 java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Channel.java create mode 100644 java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelExec.java create mode 100644 java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelSession.java create mode 100644 java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/JSch.java create mode 100644 java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Session.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll b/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll new file mode 100644 index 00000000000..c0025043fce --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll @@ -0,0 +1,32 @@ +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.ExternalProcess +import semmle.code.java.security.CommandArguments + +private class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration { + RemoteUserInputToArgumentToExecFlowConfig() { + this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig" + } + + override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType + or + node.getType() instanceof BoxedType + or + isSafeCommandArgument(node.asExpr()) + } +} + +/** + * Implementation of `ExecTainted.ql`. It is extracted to a QLL + * so that it can be excluded from `ExecUnescaped.ql` to avoid + * reporting overlapping results. + */ +predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg) { + exists(RemoteUserInputToArgumentToExecFlowConfig conf | + conf.hasFlowPath(source, sink) and sink.getNode() = DataFlow::exprNode(execArg) + ) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql new file mode 100644 index 00000000000..b73203ecbbc --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql @@ -0,0 +1,24 @@ +/** + * @name Uncontrolled command line + * @description Using externally controlled strings in a command line is vulnerable to malicious + * changes in the strings. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.ExternalProcess +import ExecCommon +import JSchOSInjection +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg +where execTainted(source, sink, execArg) +select execArg, source, sink, "$@ flows to here and is used in a command.", source.getNode(), + "User-provided value" diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp new file mode 100644 index 00000000000..57e8a61fe74 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp @@ -0,0 +1,64 @@ + + + + +

    +JSch is a pure Java implementation of SSH2. +JSch allows you to connect to a sshd server and use port forwarding, X11 forwarding, +file transfer, command execution, etc., and you can integrate its functionality into your own Java programs. +JSch is licensed under BSD style license. +If an OS command is built using an attacker-controlled data, it may allow the attacker +to run an arbitrary code on the remote host. +

    +
    + + +

    +Including an user input in a JSch lib's OS command expression should be avoided. +If this is not possible, then a strong input validation must be performed. +Some examples of an effective validation include: + +Validating against an allowlist of permitted values. +Validating that the input is a number. +Validating that the input contains only alphanumeric characters, no other syntax or whitespace. + +Never attempt to sanitize input by escaping shell metacharacters. In practice, +this is just too error-prone and vulnerable to being bypassed by a skilled attacker. + +

    +
    + + +

    +The following example uses the untrusted data to build an OS command. +

    + +
    + + +

    +The following example validates the untrusted data before build an OS command. +

    + +
    + + +
  • + JCraft: + JSch - Java Secure Channel. +
  • +
  • + OWASP: + Command Injection. +
  • +
  • + OWASP: + OS Command Injection Defense Cheat Sheet. +
  • +
  • + PortSwigger: + OS command injection. +
  • + +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll new file mode 100644 index 00000000000..ec1f4d0adfa --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll @@ -0,0 +1,20 @@ +/** + * Provides classes for JSch OS command injection detection + */ + +import java + +/** The class `com.jcraft.jsch.ChannelExec`. */ +private class JSchChannelExec extends RefType { + JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") } +} + +/** A method to set an OS Command for the execution. */ +private class ChannelExecSetCommandMethod extends Method, ExecCallable { + ChannelExecSetCommandMethod() { + this.hasName("setCommand") and + this.getDeclaringType() instanceof JSchChannelExec + } + + override int getAnExecutedArgument() { result = 0 } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java new file mode 100644 index 00000000000..ab4c3fb1c06 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java @@ -0,0 +1,17 @@ +public class JSchOSInjectionBad { + void jschOsExecution(HttpServletRequest request) { + String command = request.getParameter("command"); + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "sshHost", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + // BAD - untrusted user data is used directly in a command + ((ChannelExec) channel).setCommand("ping " + command); + + channel.connect(); + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java new file mode 100644 index 00000000000..fdd72e551a0 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java @@ -0,0 +1,46 @@ +public class JSchOSInjectionSanitized { + void jschOsExecutionPing(HttpServletRequest request) { + String untrusted = request.getParameter("command"); + + //GOOD - Validate user the input. + if (!com.google.common.net.InetAddresses.isInetAddress(untrusted)) { + System.out.println("Invalid IP address"); + return; + } + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "host", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("ping " + untrusted); + + channel.connect(); + } + + void jschOsExecutionDig(HttpServletRequest request) { + String untrusted = request.getParameter("command"); + + //GOOD - check whether the user input doesn't contain dangerous shell characters. + String[] badChars = new String[] {"^", "~" ," " , "&", "|", ";", "$", ">", "<", "`", "\\", ",", "!", "{", "}", "(", ")", "@", "%", "#", "%0A", "%0a", "\n", "\r\n"}; + + for (String badChar : badChars) { + if (untrusted.contains(badChar)) { + System.out.println("Invalid IP address"); + return; + } + } + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "host", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("dig " + untrusted); + + channel.connect(); + } +} + diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.expected b/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.expected new file mode 100644 index 00000000000..6d87a2ffb89 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.expected @@ -0,0 +1,11 @@ +edges +| JSchOSInjectionTest.java:14:30:14:60 | getParameter(...) : String | JSchOSInjectionTest.java:26:48:26:64 | ... + ... | +| JSchOSInjectionTest.java:38:30:38:60 | getParameter(...) : String | JSchOSInjectionTest.java:50:32:50:48 | ... + ... | +nodes +| JSchOSInjectionTest.java:14:30:14:60 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JSchOSInjectionTest.java:26:48:26:64 | ... + ... | semmle.label | ... + ... | +| JSchOSInjectionTest.java:38:30:38:60 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JSchOSInjectionTest.java:50:32:50:48 | ... + ... | semmle.label | ... + ... | +#select +| JSchOSInjectionTest.java:26:48:26:64 | ... + ... | JSchOSInjectionTest.java:14:30:14:60 | getParameter(...) : String | JSchOSInjectionTest.java:26:48:26:64 | ... + ... | $@ flows to here and is used in a command. | JSchOSInjectionTest.java:14:30:14:60 | getParameter(...) | User-provided value | +| JSchOSInjectionTest.java:50:32:50:48 | ... + ... | JSchOSInjectionTest.java:38:30:38:60 | getParameter(...) : String | JSchOSInjectionTest.java:50:32:50:48 | ... + ... | $@ flows to here and is used in a command. | JSchOSInjectionTest.java:38:30:38:60 | getParameter(...) | User-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.qlref b/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.qlref new file mode 100644 index 00000000000..714c3cadc5f --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/ExecTainted.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-078/ExecTainted.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/JSchOSInjectionTest.java b/java/ql/test/experimental/query-tests/security/CWE-078/JSchOSInjectionTest.java new file mode 100644 index 00000000000..08baf0a9772 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/JSchOSInjectionTest.java @@ -0,0 +1,56 @@ +import com.jcraft.jsch.*; + +import javax.servlet.http.*; +import javax.servlet.ServletException; +import java.io.IOException; + +public class JSchOSInjectionTest extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String host = "sshHost"; + String user = "user"; + String password = "password"; + String command = request.getParameter("command"); + + java.util.Properties config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + + JSch jsch = new JSch(); + Session session = jsch.getSession(user, host, 22); + session.setPassword(password); + session.setConfig(config); + session.connect(); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("ping " + command); + channel.setInputStream(null); + ((ChannelExec) channel).setErrStream(System.err); + + channel.connect(); + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String host = "sshHost"; + String user = "user"; + String password = "password"; + String command = request.getParameter("command"); + + java.util.Properties config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + + JSch jsch = new JSch(); + Session session = jsch.getSession(user, host, 22); + session.setPassword(password); + session.setConfig(config); + session.connect(); + + ChannelExec channel = (ChannelExec)session.openChannel("exec"); + channel.setCommand("ping " + command); + channel.setInputStream(null); + channel.setErrStream(System.err); + + channel.connect(); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-078/options b/java/ql/test/experimental/query-tests/security/CWE-078/options new file mode 100644 index 00000000000..eb7209ebe1e --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-078/options @@ -0,0 +1,2 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jsch-0.1.55 + diff --git a/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Channel.java b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Channel.java new file mode 100644 index 00000000000..5b084813c61 --- /dev/null +++ b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Channel.java @@ -0,0 +1,46 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public abstract class Channel implements Runnable { + public void connect() { + } + + public void setInputStream(InputStream in){ + } + + public void disconnect(){ + } + + public void run() { + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelExec.java b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelExec.java new file mode 100644 index 00000000000..6ff67754d99 --- /dev/null +++ b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelExec.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public class ChannelExec extends ChannelSession { + public void setErrStream(OutputStream out) { + } + + public void setCommand(String command){ + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelSession.java b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelSession.java new file mode 100644 index 00000000000..a6c97b334b6 --- /dev/null +++ b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/ChannelSession.java @@ -0,0 +1,33 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class ChannelSession extends Channel { +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/JSch.java b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/JSch.java new file mode 100644 index 00000000000..b8ce91b9715 --- /dev/null +++ b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/JSch.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class JSch { + + public JSch() {} + + public Session getSession(String username, String host, int port) { + return null; + } + +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Session.java b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Session.java new file mode 100644 index 00000000000..4cf2015fceb --- /dev/null +++ b/java/ql/test/experimental/stubs/jsch-0.1.55/com/jcraft/jsch/Session.java @@ -0,0 +1,55 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class Session implements Runnable { + + public Channel openChannel(String type) { + if ("exec".equals(type)) + return new ChannelExec(); + + return null; + } + + public void setPassword(String password) { + } + + public void setConfig(java.util.Properties newconf) { + } + + public void connect() { + } + + public void run(){ + } + + public void disconnect(){ + } +} \ No newline at end of file From 7455b1b4f08d15029d82a4dee6b5c1143e7b95ae Mon Sep 17 00:00:00 2001 From: Hayk Andriasyan <77549590+p0wn4j@users.noreply.github.com> Date: Mon, 26 Apr 2021 15:17:57 +0400 Subject: [PATCH 096/550] Update JSchOSInjectionSanitized.java --- .../Security/CWE/CWE-078/JSchOSInjectionSanitized.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java index fdd72e551a0..b47a2b82ed7 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java @@ -27,7 +27,7 @@ public class JSchOSInjectionSanitized { for (String badChar : badChars) { if (untrusted.contains(badChar)) { - System.out.println("Invalid IP address"); + System.out.println("Invalid host"); return; } } From b7de370918e4b193b855ccae6fa491fcf6f5726a Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 26 Apr 2021 23:04:08 +0300 Subject: [PATCH 097/550] Add files via upload --- .../Security/CWE/CWE-415/DoubleFree.ql | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql index 199dc18bee7..bb799ec4d52 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql @@ -12,40 +12,33 @@ import cpp -/** - * The function allows `getASuccessor` to be called recursively. - * This provides a stop in situations of possible influence on the pointer. - */ -ControlFlowNode recursASuccessor(FunctionCall fc, LocalScopeVariable v) { - result = fc - or - exists(ControlFlowNode mid | - mid = recursASuccessor(fc, v) and - result = mid.getASuccessor() and - not result = v.getAnAssignedValue() and - not result.(AddressOfExpr).getOperand() = v.getAnAccess() and - not ( - not result instanceof DeallocationExpr and - result.(FunctionCall).getAnArgument().(VariableAccess).getTarget() = v - ) and - ( - fc.getTarget().hasGlobalOrStdName("realloc") and - ( - not fc.getParent*() instanceof IfStmt and - not result instanceof IfStmt - ) - or - not fc.getTarget().hasGlobalOrStdName("realloc") - ) - ) -} - -from FunctionCall fc +from FunctionCall fc, FunctionCall fc2, LocalScopeVariable v where - exists(FunctionCall fc2, LocalScopeVariable v | - freeCall(fc, v.getAnAccess()) and - freeCall(fc2, v.getAnAccess()) and - fc != fc2 and - recursASuccessor(fc, v) = fc2 + freeCall(fc, v.getAnAccess()) and + freeCall(fc2, v.getAnAccess()) and + fc != fc2 and + fc.getASuccessor*() = fc2 and + not exists(Expr exptmp | + (exptmp = v.getAnAssignedValue() or exptmp.(AddressOfExpr).getOperand() = v.getAnAccess()) and + exptmp = fc.getASuccessor*() and + exptmp = fc2.getAPredecessor*() + ) and + not exists(FunctionCall fctmp | + not fctmp instanceof DeallocationExpr and + fctmp = fc.getASuccessor*() and + fctmp = fc2.getAPredecessor*() and + fctmp.getAnArgument().(VariableAccess).getTarget() = v + ) and + ( + fc.getTarget().hasGlobalOrStdName("realloc") and + ( + not fc.getParent*() instanceof IfStmt and + not exists(IfStmt iftmp | + iftmp.getCondition().getAChild*().(VariableAccess).getTarget().getAnAssignedValue() = fc + ) + ) + or + not fc.getTarget().hasGlobalOrStdName("realloc") ) -select fc.getArgument(0), "This pointer may be cleared again later." +select fc2.getArgument(0), + "This pointer may have already been cleared in the line " + fc.getLocation().getStartLine() + "." From c31a761750e68916fb2a4bb8517ea9026d4ad87c Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 26 Apr 2021 23:05:08 +0300 Subject: [PATCH 098/550] Add files via upload --- .../CWE-415/semmle/tests/DoubleFree.expected | 6 +- .../Security/CWE/CWE-415/semmle/tests/test.c | 87 +++++++++++++++++-- 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected index 669c65cba2a..0ab66e5b26c 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected @@ -1,2 +1,4 @@ -| test.c:9:8:9:10 | buf | This pointer may be cleared again later. | -| test.c:19:26:19:29 | buf1 | This pointer may be cleared again later. | +| test.c:11:16:11:18 | buf | This pointer may have already been cleared in the line 10. | +| test.c:18:8:18:10 | buf | This pointer may have already been cleared in the line 17. | +| test.c:57:8:57:10 | buf | This pointer may have already been cleared in the line 55. | +| test.c:78:8:78:10 | buf | This pointer may have already been cleared in the line 77. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c index 1ebf3546480..c9be64a645c 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c @@ -1,21 +1,96 @@ typedef unsigned long size_t; void *malloc(size_t size); void free(void *ptr); +#define NULL 0 void workFunction_0(char *s) { int intSize = 10; char *buf; buf = (char *) malloc(intSize); - free(buf); // BAD - if(buf) free(buf); + free(buf); // GOOD + if(buf) free(buf); // BAD } void workFunction_1(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // GOOD + free(buf); // BAD +} +void workFunction_2(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // GOOD + buf = NULL; + free(buf); +} +void workFunction_3(char *s) { + int intSize = 10; + char *buf; + int intFlag; + buf = (char *) malloc(intSize); + if(buf[1]%5) { + free(buf); + buf = NULL; + } + free(buf); +} +void workFunction_4(char *s) { + int intSize = 10; + char *buf; + char *tmpbuf; + tmpbuf = (char *) malloc(intSize); + buf = (char *) malloc(intSize); + free(buf); // GOOD + buf = tmpbuf; + free(buf); // GOOD +} +void workFunction_5(char *s) { + int intSize = 10; + char *buf; + int intFlag; + buf = (char *) malloc(intSize); + if(intFlag) { + free(buf); + } + free(buf); // BAD +} +void workFunction_6(char *s) { + int intSize = 10; + char *buf; + char *tmpbuf; + int intFlag; + tmpbuf = (char *) malloc(intSize); + buf = (char *) malloc(intSize); + if(intFlag) { + free(buf); + buf = tmpbuf; + } + free(buf); +} +void workFunction_7(char *s) { int intSize = 10; char *buf; char *buf1; buf = (char *) malloc(intSize); - buf1 = (char *) realloc(buf,intSize*2); - if(buf) free(buf); // GOOD - buf = (char *) realloc(buf1,intSize*4); // BAD - free(buf1); + buf1 = (char *) realloc(buf,intSize*4); + free(buf); // BAD +} +void workFunction_8(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + buf1 = (char *) realloc(buf,intSize*4); + if(!buf1) + free(buf); // GOOD +} +void workFunction_9(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + if(!(buf1 = (char *) realloc(buf,intSize*4))) + free(buf); // GOOD } From 0c3e2b9ab7e54cc6585d93132cea2a3a358f437f Mon Sep 17 00:00:00 2001 From: ihsinme Date: Tue, 27 Apr 2021 10:11:32 +0300 Subject: [PATCH 099/550] Update test.c --- .../Security/CWE/CWE-415/semmle/tests/test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c index c9be64a645c..22cd6a536f4 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c @@ -23,7 +23,7 @@ void workFunction_2(char *s) { buf = (char *) malloc(intSize); free(buf); // GOOD buf = NULL; - free(buf); + free(buf); // GOOD } void workFunction_3(char *s) { int intSize = 10; @@ -31,10 +31,10 @@ void workFunction_3(char *s) { int intFlag; buf = (char *) malloc(intSize); if(buf[1]%5) { - free(buf); + free(buf); // GOOD buf = NULL; } - free(buf); + free(buf); // GOOD } void workFunction_4(char *s) { int intSize = 10; @@ -52,7 +52,7 @@ void workFunction_5(char *s) { int intFlag; buf = (char *) malloc(intSize); if(intFlag) { - free(buf); + free(buf); // GOOD } free(buf); // BAD } @@ -64,10 +64,10 @@ void workFunction_6(char *s) { tmpbuf = (char *) malloc(intSize); buf = (char *) malloc(intSize); if(intFlag) { - free(buf); + free(buf); // GOOD buf = tmpbuf; } - free(buf); + free(buf); // GOOD } void workFunction_7(char *s) { int intSize = 10; From 044c92016bd89438f8ba135a65717f13c6d52d7b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 11:52:26 +0200 Subject: [PATCH 100/550] Data flow: Cache enclosing callable predicates --- .../dataflow/internal/DataFlowImplCommon.qll | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index a51c20c2288..b5cc2438456 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -242,6 +242,14 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + /** * Gets a viable target for the lambda call `call`. * @@ -553,7 +561,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +619,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -866,7 +874,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -1017,7 +1025,7 @@ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { exists(Node n0 | pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() + nodeEnclosingCallable(n0, pragma[only_bind_into](result)) ) } @@ -1042,7 +1050,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) From 1a56f0b79ce8edf7f7d577e599e49c2f51cdf32b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 11:58:00 +0200 Subject: [PATCH 101/550] Data flow: Cache `getNodeType` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 28 ++++++----- .../dataflow/internal/DataFlowImplCommon.qll | 48 +++++++++++-------- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..4dbf1b0cdaa 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1327,11 +1327,11 @@ private module LocalFlowBigStep { ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and @@ -1350,7 +1350,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1384,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1443,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -2088,7 +2090,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2758,7 +2760,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -3148,7 +3150,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3591,7 +3593,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3627,7 +3629,7 @@ private module FlowExploration { not fullBarrier(node, config) and not clearsContent(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3797,7 +3799,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3815,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3829,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index b5cc2438456..a6f04cb8ce9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -119,7 +119,7 @@ private module LambdaFlow { ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +129,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +146,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -168,7 +168,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -250,6 +250,9 @@ private module Cached { c = call.getEnclosingCallable() } + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + /** * Gets a viable target for the lambda call `call`. * @@ -282,7 +285,7 @@ private module Cached { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -430,10 +433,10 @@ private module Cached { then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } @@ -455,7 +458,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -511,11 +514,11 @@ private module Cached { | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -653,8 +656,8 @@ private module Cached { ) { storeStep(node1, c, node2) and readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -663,8 +666,8 @@ private module Cached { argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } @@ -784,8 +787,8 @@ private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -1023,10 +1026,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - nodeEnclosingCallable(n0, pragma[only_bind_into](result)) - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] From 8bfeae768faac60bcdc8de1cc59664d1982e728a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 13:16:54 +0200 Subject: [PATCH 102/550] Data flow: Cache `simpleLocalFlowStep` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 5 +---- .../csharp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 4dbf1b0cdaa..7bf05143cfa 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index a6f04cb8ce9..87034b74fde 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -689,8 +689,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -707,6 +708,12 @@ private module Cached { ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. From 96aa1828935984adfdaa6b50abc566c79f94e79f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 13:22:37 +0200 Subject: [PATCH 103/550] Data flow: Cache `jumpStep` --- .../semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 7bf05143cfa..938a7b3fe2f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -234,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 87034b74fde..32e0f913015 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -160,7 +160,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -253,6 +253,9 @@ private module Cached { cached predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + /** * Gets a viable target for the lambda call `call`. * From 4009c01558472839f32c006507505749bf864a57 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 15:52:22 +0200 Subject: [PATCH 104/550] Data flow: Cache `readStep` --- .../dataflow/internal/DataFlowImplCommon.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 32e0f913015..3a245cda11a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -339,7 +339,7 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or @@ -658,7 +658,7 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and + read(_, c, _) and contentType = getNodeDataFlowType(node1) and containerType = getNodeDataFlowType(node2) or @@ -668,12 +668,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and + read(n2, c, n1) and contentType = getNodeDataFlowType(n1) and containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -789,14 +792,14 @@ class CastingNode extends Node { // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with // the type of `x.f`. - readStep(_, _, this) + read(_, _, this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and + read(n1, c, n2) and container = getNodeDataFlowType(n1) and content = getNodeDataFlowType(n2) } @@ -1087,8 +1090,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { From 1bf0e01a83c224632864d4825f5d9f67b69866d0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 15:55:52 +0200 Subject: [PATCH 105/550] Data flow: Cache `clearsContent` --- .../semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 6 +++--- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 938a7b3fe2f..4099043f4d8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1258,7 +1258,7 @@ private module LocalFlowBigStep { private class FlowCheckNode extends Node { FlowCheckNode() { this instanceof CastNode or - clearsContent(this, _) + clearsContentCached(this, _) } } @@ -3610,7 +3610,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3624,7 +3624,7 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 3a245cda11a..e8260dd3c8c 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -256,6 +256,9 @@ private module Cached { cached predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + /** * Gets a viable target for the lambda call `call`. * @@ -1141,7 +1144,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { From 23113c4ff7002828421630e34febca601341a826 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Apr 2021 16:11:08 +0200 Subject: [PATCH 106/550] Data flow: Cache `isUnreachableInCall` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 6 +++--- .../code/csharp/dataflow/internal/DataFlowImplCommon.qll | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 4099043f4d8..68fde153549 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1318,7 +1318,7 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( @@ -1332,7 +1332,7 @@ private module LocalFlowBigStep { ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -3782,7 +3782,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index e8260dd3c8c..f3b005ff148 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -259,6 +259,9 @@ private module Cached { cached predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + /** * Gets a viable target for the lambda call `call`. * @@ -731,7 +734,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -753,7 +756,7 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = @@ -926,7 +929,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** From 9738de2cb9487c52580dc595bea0f8efbd3066fd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:28:20 +0200 Subject: [PATCH 107/550] Data flow: Cache `OutNodeExt` --- .../dataflow/internal/DataFlowImplCommon.qll | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index f3b005ff148..d6fbadd21f0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -262,6 +262,23 @@ private module Cached { cached predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + /** * Gets a viable target for the lambda call `call`. * @@ -970,11 +987,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -987,7 +1000,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -998,10 +1011,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -1012,13 +1021,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ From 346af4f97adf1c650ee4168352667c3de867bd69 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:30:01 +0200 Subject: [PATCH 108/550] Data flow: Cache `ReturnNodeExt` --- .../dataflow/internal/DataFlowImplCommon.qll | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index d6fbadd21f0..c81af6cde57 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -279,6 +279,17 @@ private module Cached { ) } + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + /** * Gets a viable target for the lambda call `call`. * @@ -672,8 +683,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -965,21 +975,10 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** From ade99c2c2b104d7b5cc5295553a8e82da328bcae Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:37:43 +0200 Subject: [PATCH 109/550] Data flow: Cache `Cast(ing)Node` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../dataflow/internal/DataFlowImplCommon.qll | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 68fde153549..242d5444d38 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1257,7 +1257,7 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or + castNode(this) or clearsContentCached(this, _) } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index c81af6cde57..d9e875ee281 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -118,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode + if castNode(node) or node instanceof ArgumentNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -290,6 +290,20 @@ private module Cached { ) } + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + /** * Gets a viable target for the lambda call `call`. * @@ -818,15 +832,7 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - read(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( From 7d4feaca2f421acd7241621d93e4d357bc721059 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:44:22 +0200 Subject: [PATCH 110/550] Data flow: Cache `ArgumentNode` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 42 +++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 51 ++++++++++++------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 242d5444d38..53a8ebe2d54 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -520,7 +520,7 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { exists(ParameterNode p | revFlow(p, toReturn, config) and @@ -529,7 +529,7 @@ private module Stage1 { } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -660,7 +660,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -672,7 +672,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -732,7 +732,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,7 +944,7 @@ private module Stage2 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1130,7 +1130,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -1143,7 +1143,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1242,7 +1242,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1585,7 +1585,7 @@ private module Stage3 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1771,7 +1771,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -1784,7 +1784,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2154,7 +2154,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2302,7 +2302,7 @@ private module Stage4 { DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2488,7 +2488,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | @@ -2501,7 +2501,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -3234,7 +3234,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3923,7 +3923,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -4137,7 +4137,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index d9e875ee281..f079d878dec 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -43,14 +43,14 @@ private module LambdaFlow { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNode or node instanceof ReturnNode + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -266,14 +266,14 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt } cached OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -304,6 +304,11 @@ private module Cached { read(_, _, n) } + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -332,7 +337,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -392,20 +397,20 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNodeExt arg, boolean read) { parameterValueFlowCand(p, arg, read) } @@ -431,7 +436,7 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) @@ -444,7 +449,7 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -520,13 +525,13 @@ private module Cached { ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -534,7 +539,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNode p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -542,7 +547,7 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { exists(ParameterNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) @@ -558,7 +563,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -578,7 +583,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -753,8 +758,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -976,6 +981,14 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. From 1112c0f9942f237d362aabd8afc715a5175b949a Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 15:50:00 +0200 Subject: [PATCH 111/550] Data flow: Cache `ParameterNode` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 74 ++++++++++-------- .../dataflow/internal/DataFlowImplCommon.qll | 75 ++++++++++++------- 2 files changed, 90 insertions(+), 59 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 53a8ebe2d54..ede9390bace 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -512,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -522,7 +522,7 @@ private module Stage1 { private predicate revFlowIn( DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) @@ -594,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -660,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -672,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -732,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -941,7 +943,7 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -989,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,7 +1135,7 @@ private module Stage2 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1196,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1242,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1272,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1582,7 +1586,7 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -1630,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1774,7 +1778,7 @@ private module Stage3 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1837,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2154,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2299,7 +2305,7 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { exists(ArgumentNodeExt arg, boolean allowsFieldFlow | @@ -2347,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2491,7 +2497,7 @@ private module Stage4 { DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2554,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2605,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2626,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3247,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3271,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3567,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3942,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3979,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4036,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4114,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index f079d878dec..a9a19ed322d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -283,7 +285,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNode p, int pos | + exists(ParameterNodeExt p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -296,7 +298,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNode or + n instanceof ParameterNodeExt or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -304,6 +306,11 @@ private module Cached { read(_, _, n) } + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + cached predicate argumentNode(Node n, DataFlowCall call, int pos) { n.(ArgumentNode).argumentOf(call, pos) @@ -328,7 +335,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -337,7 +344,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -379,7 +386,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -410,12 +417,14 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNodeExt arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -427,7 +436,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -438,7 +447,7 @@ private module Cached { private predicate argumentValueFlowsThroughCand0( DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -456,7 +465,7 @@ private module Cached { ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -483,7 +492,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -497,7 +506,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -522,7 +531,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read exists(ArgumentNodeExt arg | @@ -539,7 +548,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNodeExt arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -549,7 +558,7 @@ private module Cached { private predicate argumentValueFlowsThrough0( DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -596,7 +605,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -702,7 +711,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -807,7 +816,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -922,7 +931,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -981,6 +990,20 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + /** A data-flow node that represents a call argument. */ class ArgumentNodeExt extends Node { ArgumentNodeExt() { argumentNode(this, _, _) } From 0c8886967bb53b7ac3c5f81b4650aa22fcc09764 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 16:38:17 +0200 Subject: [PATCH 112/550] Data flow: Cache `nodeIsHidden` --- .../code/csharp/dataflow/internal/DataFlowImpl.qll | 2 +- .../csharp/dataflow/internal/DataFlowImplCommon.qll | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index ede9390bace..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2986,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index a9a19ed322d..b5ceabc605d 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -244,6 +244,14 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + cached predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } @@ -271,6 +279,9 @@ private module Cached { n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt } + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + cached OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) From 914184f3dd08969b0709104be32287a6779ad243 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 17:02:20 +0200 Subject: [PATCH 113/550] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../cpp/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/internal/DataFlowImplLocal.qll | 155 ++++----- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../ir/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../dataflow/internal/DataFlowImpl5.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl2.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl3.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl4.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl5.qll | 155 ++++----- .../java/dataflow/internal/DataFlowImpl6.qll | 155 ++++----- .../dataflow/internal/DataFlowImplCommon.qll | 313 +++++++++++------- .../dataflow/new/internal/DataFlowImpl.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl2.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl3.qll | 155 ++++----- .../dataflow/new/internal/DataFlowImpl4.qll | 155 ++++----- .../new/internal/DataFlowImplCommon.qll | 313 +++++++++++------- 27 files changed, 2659 insertions(+), 2158 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index a51c20c2288..b5ceabc605d 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index a51c20c2288..b5ceabc605d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index a51c20c2288..b5ceabc605d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 9498e51e7e6..cf83530bcbd 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,7 +512,7 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) @@ -523,16 +520,16 @@ private module Stage1 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -597,7 +594,9 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -663,7 +662,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +674,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,7 +734,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and @@ -944,10 +943,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -992,7 +991,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1133,10 +1132,10 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1146,7 +1145,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1199,13 +1198,15 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1245,7 +1246,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and @@ -1260,8 +1261,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1276,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParameterNodeExt or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1322,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1351,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1385,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1444,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1583,10 +1586,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1631,7 +1634,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1772,10 +1775,10 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1785,7 +1788,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1838,13 +1841,15 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2088,7 +2093,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2155,7 +2160,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, + DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and @@ -2300,10 +2305,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgumentNodeExt arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2348,7 +2353,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2489,10 +2494,10 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, + DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNode p, boolean allowsFieldFlow | + exists(ParameterNodeExt p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2502,7 +2507,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2555,13 +2560,15 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough( + ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config + ) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2606,7 +2613,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2627,7 +2634,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParameterNodeExt p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2758,7 +2765,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2979,7 +2986,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3148,7 +3155,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3235,7 +3242,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3248,7 +3255,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParameterNodeExt p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3272,7 +3279,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3568,7 +3575,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParameterNodeExt p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3591,7 +3598,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3611,7 +3618,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3625,9 +3632,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3783,7 +3790,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3797,7 +3804,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3813,7 +3820,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3827,7 +3834,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3924,7 +3931,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3943,7 +3950,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3980,7 +3987,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4037,7 +4044,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParameterNodeExt p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4115,7 +4122,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParameterNodeExt p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4138,7 +4145,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index a51c20c2288..b5ceabc605d 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -35,22 +35,24 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda( + DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg + ) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +120,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +131,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +148,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +162,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +170,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +178,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,8 +229,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, - DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -242,6 +244,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgumentNodeExt arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParameterNodeExt p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParameterNodeExt or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +346,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +355,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +397,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { p = node and read = false or @@ -325,30 +410,32 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand( + ParameterNodeExt p, ArgumentNodeExt arg, boolean read + ) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +447,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +456,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +469,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParameterNodeExt p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +503,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +534,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,16 +542,16 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgumentNodeExt arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) @@ -472,7 +559,7 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read + ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read ) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) @@ -480,9 +567,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParameterNodeExt param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +583,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +603,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +616,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +640,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +698,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +722,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +730,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +740,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +767,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +778,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgumentNodeExt).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +800,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +822,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +857,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +942,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +954,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +987,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +1001,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParameterNodeExt extends Node { + ParameterNodeExt() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgumentNodeExt extends Node { + ArgumentNodeExt() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1039,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1052,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1063,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1073,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1099,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1129,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1150,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1201,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { From befc80b3cbe8ac7899a2e5b023ba2a39d5221099 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 17:02:28 +0200 Subject: [PATCH 114/550] C#: Update data-flow caching --- csharp/ql/src/semmle/code/csharp/Caching.qll | 38 -- .../dataflow/internal/DataFlowDispatch.qll | 49 +- .../dataflow/internal/DataFlowPrivate.qll | 582 ++++++++---------- .../dataflow/internal/DataFlowPublic.qll | 12 +- .../internal/TaintTrackingPrivate.qll | 7 +- 5 files changed, 304 insertions(+), 384 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 374ecaaa183..92417e34586 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -47,44 +47,6 @@ module Stages { } } - cached - module DataFlowStage { - private import semmle.code.csharp.dataflow.internal.DataFlowDispatch - private import semmle.code.csharp.dataflow.internal.DataFlowPrivate - private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon - private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate - - cached - predicate forceCachingInSameStage() { any() } - - cached - private predicate forceCachingInSameStageRev() { - defaultAdditionalTaintStep(_, _) - or - any(ArgumentNode n).argumentOf(_, _) - or - exists(any(DataFlow::Node n).getEnclosingCallable()) - or - exists(any(DataFlow::Node n).getControlFlowNode()) - or - exists(any(DataFlow::Node n).getType()) - or - exists(any(NodeImpl n).getDataFlowType()) - or - exists(any(DataFlow::Node n).getLocation()) - or - exists(any(DataFlow::Node n).toString()) - or - exists(any(OutNode n).getCall(_)) - or - exists(CallContext cc) - or - exists(any(DataFlowCall c).getEnclosingCallable()) - or - forceCachingInSameStageRev() - } - } - cached module UnificationStage { private import semmle.code.csharp.Unification diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 0a7cf4acc41..04465f5ae9e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -1,10 +1,10 @@ private import csharp private import cil private import dotnet +private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowPublic private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl -private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.FlowSummary private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.frameworks.system.Collections @@ -68,31 +68,30 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c ) } -cached -private module Cached { - cached - newtype TReturnKind = - TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or - TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or - TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or - TImplicitCapturedReturnKind(LocalScopeVariable v) { - exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | - v = def.getSourceVariable().getAssignable() - ) - } or - TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { - rk instanceof NormalReturnKind and - ( - target instanceof Constructor or - not target.getReturnType() instanceof VoidType - ) - or - exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) - } +newtype TReturnKind = + TNormalReturnKind() or + TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or + TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or + TImplicitCapturedReturnKind(LocalScopeVariable v) { + exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | + v = def.getSourceVariable().getAssignable() + ) + } or + TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { + rk instanceof NormalReturnKind and + ( + target instanceof Constructor or + not target.getReturnType() instanceof VoidType + ) + or + exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) + } +private module Cached { cached newtype TDataFlowCall = TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) { + DataFlowImplCommon::forceCachingInSameStage() and cfn.getElement() = dc.getCall() } or TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) { @@ -246,7 +245,6 @@ abstract class DataFlowCall extends TDataFlowCall { abstract DataFlow::Node getNode(); /** Gets the enclosing callable of this call. */ - cached abstract DataFlowCallable getEnclosingCallable(); /** Gets the underlying expression, if any. */ @@ -280,10 +278,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall { override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn } - override DataFlowCallable getEnclosingCallable() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = cfn.getEnclosingCallable() - } + override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() } override string toString() { result = cfn.toString() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index f42410ca3a5..c1b15df72dc 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -7,7 +7,6 @@ private import DataFlowImplCommon private import ControlFlowReachability private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.dataflow.FlowSummary -private import semmle.code.csharp.Caching private import semmle.code.csharp.Conversion private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl private import semmle.code.csharp.ExprOrStmtParent @@ -21,7 +20,6 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks abstract class NodeImpl extends Node { /** Do not call: use `getEnclosingCallable()` instead. */ - cached abstract DataFlowCallable getEnclosingCallableImpl(); /** Do not call: use `getType()` instead. */ @@ -29,9 +27,8 @@ abstract class NodeImpl extends Node { abstract DotNet::Type getTypeImpl(); /** Gets the type of this node used for type pruning. */ - cached Gvn::GvnType getDataFlowType() { - Stages::DataFlowStage::forceCachingInSameStage() and + forceCachingInSameStage() and exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | t0 = getCSharpType(this.getType()) or @@ -55,26 +52,25 @@ abstract class NodeImpl extends Node { private class ExprNodeImpl extends ExprNode, NodeImpl { override DataFlowCallable getEnclosingCallableImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getEnclosingCallable() } override DotNet::Type getTypeImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and + forceCachingInSameStage() and result = this.getExpr().getType() } override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and this = TExprNode(result) + forceCachingInSameStage() and this = TExprNode(result) } override Location getLocationImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getLocation() + forceCachingInSameStage() and result = this.getExpr().getLocation() } override string toStringImpl() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.getControlFlowNode().toString() + forceCachingInSameStage() and + result = this.getControlFlowNodeImpl().toString() or exists(CIL::Expr e | this = TCilExprNode(e) and @@ -394,6 +390,22 @@ module LocalFlow { } } +/** + * This is the local flow predicate that is used as a building block in global + * data flow. It excludes SSA flow through instance fields, as flow through fields + * is handled by the global data-flow library, but includes various other steps + * that are only relevant for global flow. + */ +predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or + LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) + or + nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) +} + pragma[noinline] private Expr getImplicitArgument(Call c, int pos) { result = c.getArgument(pos) and @@ -582,12 +594,16 @@ private Type getCSharpType(DotNet::Type t) { result.matchesHandle(t) } +private class RelevantDataFlowType extends DataFlowType { + RelevantDataFlowType() { this = any(NodeImpl n).getDataFlowType() } +} + /** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */ private class DataFlowTypeOrUnifiable extends Gvn::GvnType { pragma[nomagic] DataFlowTypeOrUnifiable() { - this instanceof DataFlowType or - Gvn::unifiable(any(DataFlowType t), this) + this instanceof RelevantDataFlowType or + Gvn::unifiable(any(RelevantDataFlowType t), this) } } @@ -598,7 +614,7 @@ private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private TypeParameter getATypeParameterSubTypeRestricted(DataFlowType t) { +private TypeParameter getATypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getATypeParameterSubType(t) } @@ -614,17 +630,30 @@ private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private Gvn::GvnType getANonTypeParameterSubTypeRestricted(DataFlowType t) { +private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getANonTypeParameterSubType(t) } /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { + private import TaintTrackingPrivate as TaintTrackingPrivate + + // Add artificial dependencies to enforce all cached predicates are evaluated + // in the "DataFlowImplCommon stage" + private predicate forceCaching() { + TaintTrackingPrivate::forceCachingInSameStage() or + exists(any(NodeImpl n).getTypeImpl()) or + exists(any(NodeImpl n).getControlFlowNodeImpl()) or + exists(any(NodeImpl n).getLocationImpl()) or + exists(any(NodeImpl n).toStringImpl()) + } + cached newtype TNode = TExprNode(ControlFlow::Nodes::ElementNode cfn) { - Stages::DataFlowStage::forceCachingInSameStage() and cfn.getElement() instanceof Expr + forceCaching() and + cfn.getElement() instanceof Expr } or TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or TSsaDefinitionNode(Ssa::Definition def) { @@ -679,23 +708,6 @@ private module Cached { callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode() } - /** - * This is the local flow predicate that is used as a building block in global - * data flow. It excludes SSA flow through instance fields, as flow through fields - * is handled by the global data-flow library, but includes various other steps - * that are only relevant for global flow. - */ - cached - predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) - or - LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) - or - nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) - } - /** * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. @@ -714,178 +726,14 @@ private module Cached { FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true) } - /** - * Holds if `pred` can flow to `succ`, by jumping from one callable to - * another. Additional steps specified by the configuration are *not* - * taken into account. - */ - cached - predicate jumpStepImpl(Node pred, Node succ) { - pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ - or - exists(FieldOrProperty fl, FieldOrPropertyRead flr | - fl.isStatic() and - fl.isFieldLike() and - fl.getAnAssignedValue() = pred.asExpr() and - fl.getAnAccess() = flr and - flr = succ.asExpr() and - flr.hasNonlocalValue() - ) - or - exists(JumpReturnKind jrk, DataFlowCall call | - FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and - viableCallable(call) = jrk.getTarget() and - succ = getAnOutNode(call, jrk.getTargetReturnKind()) - ) - } - cached newtype TContent = TFieldContent(Field f) { f.isUnboundDeclaration() } or TPropertyContent(Property p) { p.isUnboundDeclaration() } or TElementContent() - /** - * Holds if data can flow from `node1` to `node2` via an assignment to - * content `c`. - */ - cached - predicate storeStepImpl(Node node1, Content c, Node node2) { - exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | - hasNodePath(x, node1, node) and - if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 - | - fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) - or - arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent - ) - or - exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | - x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and - node2 = TParamsArgumentNode(callCfn) and - isParamsArg(_, arg, _) and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(AsyncReturnNode).getExpr() = e and - c = getResultContent() - ) - or - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) - } - pragma[nomagic] - private PropertyContent getResultContent() { - result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() - } - - /** - * Holds if data can flow from `node1` to `node2` via a read of content `c`. - */ - cached - predicate readStepImpl(Node node1, Content c, Node node2) { - exists(ReadStepConfiguration x | - hasNodePath(x, node1, node2) and - fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) - or - hasNodePath(x, node1, node2) and - arrayRead(node1.asExpr(), node2.asExpr()) and - c instanceof ElementContent - or - exists(ForeachStmt fs, Ssa::ExplicitDefinition def | - x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), - def.getControlFlowNode()) and - node2.(SsaDefinitionNode).getDefinition() = def and - c instanceof ElementContent - ) - or - hasNodePath(x, node1, node2) and - node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and - c = getResultContent() - or - // node1 = (..., node2, ...) - // node1.ItemX flows to node2 - exists(TupleExpr te, int i, Expr item | - te = node1.asExpr() and - not te.isConstruction() and - c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and - // node1 = (..., item, ...) - te.getArgument(i) = item - | - // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) - node2.asExpr().(TupleExpr) = item and - hasNodePath(x, node1, node2) - or - // item = variable in node1 = (..., variable, ...) - exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = tad and - tad.getLeaf() = item and - hasNodePath(x, node1, node2) - ) - or - // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) - te = any(PatternExpr pe).getAChildExpr*() and - exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = lvd and - lvd.getDeclaration() = item and - hasNodePath(x, node1, node2) - ) - ) - ) - or - FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) - } - - /** - * 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` - * in `x.f = newValue`. - */ - cached - predicate clearsContent(Node n, Content c) { - fieldOrPropertyStore(_, c, _, n.asExpr(), true) - or - fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) - or - FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and - not c instanceof ElementContent - or - FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) - or - exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | - oi = we.getInitializer() and - n.asExpr() = oi and - f = oi.getAMemberInitializer().getInitializedMember() and - c = f.getContent() - ) - } - - /** - * Holds if the node `n` is unreachable when the call context is `call`. - */ - cached - predicate isUnreachableInCall(Node n, DataFlowCall call) { - exists( - ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs - | - viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and - paramNode.getSsaDefinition().getARead() = guard and - guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) - ) - } - - pragma[nomagic] - private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, DataFlowType t2) { + private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantDataFlowType t2) { not t1 instanceof Gvn::TypeParameterGvnType and t1 = t2 or @@ -899,102 +747,53 @@ private module Cached { * `t2` are allowed to be type parameters. */ cached - predicate commonSubType(DataFlowType t1, DataFlowType t2) { commonSubTypeGeneral(t1, t2) } + predicate commonSubType(RelevantDataFlowType t1, RelevantDataFlowType t2) { + commonSubTypeGeneral(t1, t2) + } cached - predicate commonSubTypeUnifiableLeft(DataFlowType t1, DataFlowType t2) { + predicate commonSubTypeUnifiableLeft(RelevantDataFlowType t1, RelevantDataFlowType t2) { exists(Gvn::GvnType t | Gvn::unifiable(t1, t) and commonSubTypeGeneral(t, t2) ) } - - cached - predicate outRefReturnNode(Ssa::ExplicitDefinition def, OutRefReturnKind kind) { - exists(Parameter p | - def.isLiveOutRefParameterDefinition(p) and - kind.getPosition() = p.getPosition() - | - p.isOut() and kind instanceof OutReturnKind - or - p.isRef() and kind instanceof RefReturnKind - ) - } - - cached - predicate summaryOutNodeCached(DataFlowCall c, Node out, ReturnKind rk) { - FlowSummaryImpl::Private::summaryOutNode(c, out, rk) - } - - cached - predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i) - } - - cached - predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) { - FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre) - } - - cached - predicate summaryReturnNodeCached(Node ret, ReturnKind rk) { - FlowSummaryImpl::Private::summaryReturnNode(ret, rk) and - not rk instanceof JumpReturnKind - } - - cached - predicate castNode(Node n) { - n.asExpr() instanceof Cast - or - n.(AssignableDefinitionNode).getDefinition() instanceof AssignableDefinitions::PatternDefinition - } - - /** Holds if `n` should be hidden from path explanations. */ - cached - predicate nodeIsHidden(Node n) { - exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | - def instanceof Ssa::PhiNode - or - def instanceof Ssa::ImplicitEntryDefinition - or - def instanceof Ssa::ImplicitCallDefinition - ) - or - exists(Parameter p | - p = n.(ParameterNode).getParameter() and - not p.fromSource() - ) - or - n = TInstanceParameterNode(any(Callable c | not c.fromSource())) - or - n instanceof YieldReturnNode - or - n instanceof AsyncReturnNode - or - n instanceof ImplicitCapturedArgumentNode - or - n instanceof MallocNode - or - n instanceof SummaryNode - or - n instanceof ParamsArgumentNode - or - n.asExpr() = any(WithExpr we).getInitializer() - } - - cached - predicate parameterNode(Node n, DataFlowCallable c, int i) { - n.(ParameterNodeImpl).isParameterOf(c, i) - } - - cached - predicate argumentNode(Node n, DataFlowCall call, int pos) { - n.(ArgumentNodeImpl).argumentOf(call, pos) - } } import Cached +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { + exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | + def instanceof Ssa::PhiNode + or + def instanceof Ssa::ImplicitEntryDefinition + or + def instanceof Ssa::ImplicitCallDefinition + ) + or + exists(Parameter p | + p = n.(ParameterNode).getParameter() and + not p.fromSource() + ) + or + n = TInstanceParameterNode(any(Callable c | not c.fromSource())) + or + n instanceof YieldReturnNode + or + n instanceof AsyncReturnNode + or + n instanceof ImplicitCapturedArgumentNode + or + n instanceof MallocNode + or + n instanceof SummaryNode + or + n instanceof ParamsArgumentNode + or + n.asExpr() = any(WithExpr we).getInitializer() +} + /** An SSA definition, viewed as a node in a data flow graph. */ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { Ssa::Definition def; @@ -1142,10 +941,12 @@ import ParameterNodes /** A data-flow node that represents a call argument. */ class ArgumentNode extends Node { - ArgumentNode() { argumentNode(this, _, _) } + ArgumentNode() { this instanceof ArgumentNodeImpl } /** Holds if this argument occurs at the given position in the given call. */ - final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } + final predicate argumentOf(DataFlowCall call, int pos) { + this.(ArgumentNodeImpl).argumentOf(call, pos) + } } abstract private class ArgumentNodeImpl extends Node { @@ -1310,14 +1111,10 @@ private module ArgumentNodes { } private class SummaryArgumentNode extends SummaryNode, ArgumentNodeImpl { - private DataFlowCall c; - private int i; - - SummaryArgumentNode() { summaryArgumentNodeCached(c, this, i) } + SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) } override predicate argumentOf(DataFlowCall call, int pos) { - call = c and - i = pos + FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos) } } } @@ -1352,7 +1149,16 @@ private module ReturnNodes { class OutRefReturnNode extends ReturnNode, SsaDefinitionNode { OutRefReturnKind kind; - OutRefReturnNode() { outRefReturnNode(this.getDefinition(), kind) } + OutRefReturnNode() { + exists(Parameter p | + this.getDefinition().isLiveOutRefParameterDefinition(p) and + kind.getPosition() = p.getPosition() + | + p.isOut() and kind instanceof OutReturnKind + or + p.isRef() and kind instanceof RefReturnKind + ) + } override ReturnKind getKind() { result = kind } } @@ -1449,7 +1255,10 @@ private module ReturnNodes { private class SummaryReturnNode extends SummaryNode, ReturnNode { private ReturnKind rk; - SummaryReturnNode() { summaryReturnNodeCached(this, rk) } + SummaryReturnNode() { + FlowSummaryImpl::Private::summaryReturnNode(this, rk) and + not rk instanceof JumpReturnKind + } override ReturnKind getKind() { result = rk } } @@ -1460,7 +1269,6 @@ import ReturnNodes /** A data-flow node that represents the output of a call. */ abstract class OutNode extends Node { /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ - cached abstract DataFlowCall getCall(ReturnKind kind); } @@ -1488,7 +1296,6 @@ private module OutNodes { } override DataFlowCall getCall(ReturnKind kind) { - Stages::DataFlowStage::forceCachingInSameStage() and result = call and ( kind instanceof NormalReturnKind and @@ -1564,14 +1371,10 @@ private module OutNodes { } private class SummaryOutNode extends SummaryNode, OutNode { - private DataFlowCall c; - private ReturnKind rk; - - SummaryOutNode() { summaryOutNodeCached(c, this, rk) } + SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) } override DataFlowCall getCall(ReturnKind kind) { - result = c and - kind = rk + FlowSummaryImpl::Private::summaryOutNode(result, this, kind) } } } @@ -1654,7 +1457,29 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead } } -predicate jumpStep = jumpStepImpl/2; +/** + * Holds if `pred` can flow to `succ`, by jumping from one callable to + * another. Additional steps specified by the configuration are *not* + * taken into account. + */ +predicate jumpStep(Node pred, Node succ) { + pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ + or + exists(FieldOrProperty fl, FieldOrPropertyRead flr | + fl.isStatic() and + fl.isFieldLike() and + fl.getAnAssignedValue() = pred.asExpr() and + fl.getAnAccess() = flr and + flr = succ.asExpr() and + flr.hasNonlocalValue() + ) + or + exists(JumpReturnKind jrk, DataFlowCall call | + FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and + viableCallable(call) = jrk.getTarget() and + succ = getAnOutNode(call, jrk.getTargetReturnKind()) + ) +} private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration { StoreStepConfiguration() { this = "StoreStepConfiguration" } @@ -1675,7 +1500,46 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio } } -predicate storeStep = storeStepImpl/3; +pragma[nomagic] +private PropertyContent getResultContent() { + result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() +} + +/** + * Holds if data can flow from `node1` to `node2` via an assignment to + * content `c`. + */ +predicate storeStep(Node node1, Content c, Node node2) { + exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | + hasNodePath(x, node1, node) and + if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 + | + fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) + or + arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent + ) + or + exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | + x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and + node2 = TParamsArgumentNode(callCfn) and + isParamsArg(_, arg, _) and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(AsyncReturnNode).getExpr() = e and + c = getResultContent() + ) + or + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) +} private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration { ReadStepConfiguration() { this = "ReadStepConfiguration" } @@ -1742,7 +1606,99 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration } } -predicate readStep = readStepImpl/3; +/** + * Holds if data can flow from `node1` to `node2` via a read of content `c`. + */ +predicate readStep(Node node1, Content c, Node node2) { + exists(ReadStepConfiguration x | + hasNodePath(x, node1, node2) and + fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) + or + hasNodePath(x, node1, node2) and + arrayRead(node1.asExpr(), node2.asExpr()) and + c instanceof ElementContent + or + exists(ForeachStmt fs, Ssa::ExplicitDefinition def | + x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), + def.getControlFlowNode()) and + node2.(SsaDefinitionNode).getDefinition() = def and + c instanceof ElementContent + ) + or + hasNodePath(x, node1, node2) and + node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and + c = getResultContent() + or + // node1 = (..., node2, ...) + // node1.ItemX flows to node2 + exists(TupleExpr te, int i, Expr item | + te = node1.asExpr() and + not te.isConstruction() and + c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and + // node1 = (..., item, ...) + te.getArgument(i) = item + | + // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) + node2.asExpr().(TupleExpr) = item and + hasNodePath(x, node1, node2) + or + // item = variable in node1 = (..., variable, ...) + exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = tad and + tad.getLeaf() = item and + hasNodePath(x, node1, node2) + ) + or + // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) + te = any(PatternExpr pe).getAChildExpr*() and + exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = lvd and + lvd.getDeclaration() = item and + hasNodePath(x, node1, node2) + ) + ) + ) + or + FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) +} + +/** + * 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` + * in `x.f = newValue`. + */ +predicate clearsContent(Node n, Content c) { + fieldOrPropertyStore(_, c, _, n.asExpr(), true) + or + fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) + or + FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and + not c instanceof ElementContent + or + FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) + or + exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | + oi = we.getInitializer() and + n.asExpr() = oi and + f = oi.getAMemberInitializer().getInitializedMember() and + c = f.getContent() + ) +} + +/** + * Holds if the node `n` is unreachable when the call context is `call`. + */ +predicate isUnreachableInCall(Node n, DataFlowCall call) { + exists( + ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs + | + viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and + paramNode.getSsaDefinition().getARead() = guard and + guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) + ) +} /** * An entity used to represent the type of data-flow node. Two nodes will have @@ -1753,10 +1709,7 @@ predicate readStep = readStepImpl/3; * `DataFlowType`, while `Func` and `Func` are not, because * `string` is not a type parameter. */ -class DataFlowType extends Gvn::GvnType { - pragma[nomagic] - DataFlowType() { this = any(NodeImpl n).getDataFlowType() } -} +class DataFlowType = Gvn::GvnType; /** Gets the type of `n` used for type pruning. */ pragma[inline] @@ -1888,11 +1841,11 @@ private module PostUpdateNodes { } private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { - private Node pre; + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, _) } - SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) } - - override Node getPreUpdateNode() { result = pre } + override Node getPreUpdateNode() { + FlowSummaryImpl::Private::summaryPostUpdateNode(this, result) + } } } @@ -1900,7 +1853,12 @@ private import PostUpdateNodes /** A node that performs a type cast. */ class CastNode extends Node { - CastNode() { castNode(this) } + CastNode() { + this.asExpr() instanceof Cast + or + this.(AssignableDefinitionNode).getDefinition() instanceof + AssignableDefinitions::PatternDefinition + } } class DataFlowExpr = DotNet::Expr; diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index 12a62faf387..bfc2f5469d0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -67,6 +67,8 @@ class Node extends TNode { } } +private class TExprNode_ = TExprNode or TCilExprNode; + /** * An expression, viewed as a node in a data flow graph. * @@ -74,9 +76,7 @@ class Node extends TNode { * to multiple `ExprNode`s, just like it may correspond to multiple * `ControlFlow::Node`s. */ -class ExprNode extends Node { - ExprNode() { this = TExprNode(_) or this = TCilExprNode(_) } - +class ExprNode extends Node, TExprNode_ { /** Gets the expression corresponding to this node. */ DotNet::Expr getExpr() { result = this.getExprAtNode(_) @@ -99,7 +99,7 @@ class ExprNode extends Node { * flow graph. */ class ParameterNode extends Node { - ParameterNode() { parameterNode(this, _, _) } + ParameterNode() { this instanceof ParameterNodeImpl } /** Gets the parameter corresponding to this node, if any. */ DotNet::Parameter getParameter() { @@ -110,7 +110,9 @@ class ParameterNode extends Node { * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } + predicate isParameterOf(DataFlowCallable c, int i) { + this.(ParameterNodeImpl).isParameterOf(c, i) + } } /** A definition, viewed as a node in a data flow graph. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 72525d3234a..462838abcd1 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -1,6 +1,5 @@ private import csharp private import TaintTrackingPublic -private import DataFlowImplCommon private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.internal.DataFlowPrivate @@ -79,7 +78,6 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon } private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - Stages::DataFlowStage::forceCachingInSameStage() and hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo) or localTaintStepCil(nodeFrom, nodeTo) @@ -87,6 +85,11 @@ private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node n cached private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. From bd0a196a397cd30d92f45b8d7e5db5dbfa2733c3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 26 Apr 2021 20:19:43 +0200 Subject: [PATCH 115/550] Java: Update data-flow caching --- .../java/dataflow/internal/DataFlowNodes.qll | 109 +++++++----------- .../dataflow/internal/DataFlowPrivate.qll | 1 - .../java/dataflow/internal/DataFlowUtil.qll | 2 + .../dataflow/internal/TaintTrackingUtil.qll | 108 +++++++++-------- 4 files changed, 107 insertions(+), 113 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll index 552849c2c34..c47efa287ad 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowNodes.qll @@ -4,71 +4,48 @@ private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dataflow.TypeFlow private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl +private import DataFlowImplCommon as DataFlowImplCommon cached -private module Cached { - cached - newtype TNode = - TExprNode(Expr e) { - not e.getType() instanceof VoidType and - not e.getParent*() instanceof Annotation - } or - TExplicitParameterNode(Parameter p) { - exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable - } or - TImplicitVarargsArray(Call c) { - c.getCallee().isVarargs() and - not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray()) - } or - TInstanceParameterNode(Callable c) { - (exists(c.getBody()) or c instanceof SummarizedCallable) and - not c.isStatic() - } or - TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or - TMallocNode(ClassInstanceExpr cie) or - TExplicitExprPostUpdate(Expr e) { - explicitInstanceArgument(_, e) - or - e instanceof Argument and not e.getType() instanceof ImmutableType - or - exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier()) - or - exists(ArrayAccess aa | e = aa.getArray()) - } or - TImplicitExprPostUpdate(InstanceAccessExt ia) { - implicitInstanceArgument(_, ia) - or - exists(FieldAccess fa | - fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa) - ) - } or - TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { - FlowSummaryImpl::Private::summaryNodeRange(c, state) - } - - cached - predicate summaryOutNodeCached(DataFlowCall c, Node out) { - FlowSummaryImpl::Private::summaryOutNode(c, out, _) +newtype TNode = + TExprNode(Expr e) { + DataFlowImplCommon::forceCachingInSameStage() and + not e.getType() instanceof VoidType and + not e.getParent*() instanceof Annotation + } or + TExplicitParameterNode(Parameter p) { + exists(p.getCallable().getBody()) or p.getCallable() instanceof SummarizedCallable + } or + TImplicitVarargsArray(Call c) { + c.getCallee().isVarargs() and + not exists(Argument arg | arg.getCall() = c and arg.isExplicitVarargsArray()) + } or + TInstanceParameterNode(Callable c) { + (exists(c.getBody()) or c instanceof SummarizedCallable) and + not c.isStatic() + } or + TImplicitInstanceAccess(InstanceAccessExt ia) { not ia.isExplicit(_) } or + TMallocNode(ClassInstanceExpr cie) or + TExplicitExprPostUpdate(Expr e) { + explicitInstanceArgument(_, e) + or + e instanceof Argument and not e.getType() instanceof ImmutableType + or + exists(FieldAccess fa | fa.getField() instanceof InstanceField and e = fa.getQualifier()) + or + exists(ArrayAccess aa | e = aa.getArray()) + } or + TImplicitExprPostUpdate(InstanceAccessExt ia) { + implicitInstanceArgument(_, ia) + or + exists(FieldAccess fa | + fa.getField() instanceof InstanceField and ia.isImplicitFieldQualifier(fa) + ) + } or + TSummaryInternalNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { + FlowSummaryImpl::Private::summaryNodeRange(c, state) } - cached - predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i) - } - - cached - predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) { - FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre) - } - - cached - predicate summaryReturnNodeCached(Node ret) { - FlowSummaryImpl::Private::summaryReturnNode(ret, _) - } -} - -private import Cached - private predicate explicitInstanceArgument(Call call, Expr instarg) { call instanceof MethodAccess and instarg = call.getQualifier() and @@ -404,13 +381,15 @@ module Private { override string toString() { result = "[summary] " + state + " in " + c } /** Holds if this summary node is the `i`th argument of `call`. */ - predicate isArgumentOf(DataFlowCall call, int i) { summaryArgumentNodeCached(call, this, i) } + predicate isArgumentOf(DataFlowCall call, int i) { + FlowSummaryImpl::Private::summaryArgumentNode(call, this, i) + } /** Holds if this summary node is a return node. */ - predicate isReturn() { summaryReturnNodeCached(this) } + predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this, _) } /** Holds if this summary node is an out node for `call`. */ - predicate isOut(DataFlowCall call) { summaryOutNodeCached(call, this) } + predicate isOut(DataFlowCall call) { FlowSummaryImpl::Private::summaryOutNode(call, this, _) } } SummaryNode getSummaryNode(SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) { @@ -439,7 +418,7 @@ private class MallocNode extends Node, TMallocNode { private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { private Node pre; - SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) } + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) } override Node getPreUpdateNode() { result = pre } } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index 23d84c10c9f..5756c56ba6d 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -284,7 +284,6 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode { /** * Holds if the node `n` is unreachable when the call context is `call`. */ -cached predicate isUnreachableInCall(Node n, DataFlowCall call) { exists( ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param, diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll index 2ad09574015..e25ab24cfbb 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll @@ -11,6 +11,7 @@ private import semmle.code.java.dataflow.FlowSteps private import semmle.code.java.dataflow.FlowSummary import semmle.code.java.dataflow.InstanceAccess private import FlowSummaryImpl as FlowSummaryImpl +private import TaintTrackingUtil as TaintTrackingUtil import DataFlowNodes::Public /** Holds if `n` is an access to an unqualified `this` at `cfgnode`. */ @@ -112,6 +113,7 @@ predicate localFlowStep(Node node1, Node node2) { */ cached predicate simpleLocalFlowStep(Node node1, Node node2) { + TaintTrackingUtil::forceCachingInSameStage() and // Variable flow steps through adjacent def-use and use-use pairs. exists(SsaExplicitUpdate upd | upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or diff --git a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index f9278ab815e..8c514e357d2 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -29,55 +29,69 @@ predicate localExprTaint(Expr src, Expr sink) { localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink)) } -/** - * Holds if taint can flow in one local step from `src` to `sink`. - */ -predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { - DataFlow::localFlowStep(src, sink) or - localAdditionalTaintStep(src, sink) or - // Simple flow through library code is included in the exposed local - // step relation, even though flow is technically inter-procedural - FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false) +cached +private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + + /** + * Holds if taint can flow in one local step from `src` to `sink`. + */ + cached + predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) { + DataFlow::localFlowStep(src, sink) or + localAdditionalTaintStep(src, sink) or + // Simple flow through library code is included in the exposed local + // step relation, even though flow is technically inter-procedural + FlowSummaryImpl::Private::Steps::summaryThroughStep(src, sink, false) + } + + /** + * Holds if taint can flow in one local step from `src` to `sink` excluding + * local data flow steps. That is, `src` and `sink` are likely to represent + * different objects. + */ + cached + predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { + localAdditionalTaintExprStep(src.asExpr(), sink.asExpr()) + or + localAdditionalTaintUpdateStep(src.asExpr(), + sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) + or + exists(Argument arg | + src.asExpr() = arg and + arg.isVararg() and + sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() + ) + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) + } + + /** + * Holds if the additional step from `src` to `sink` should be included in all + * global taint flow configurations. + */ + cached + predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { + localAdditionalTaintStep(src, sink) or + any(AdditionalTaintStep a).step(src, sink) + } + + /** + * Holds if `node` should be a sanitizer in all global taint flow configurations + * but not in local taint. + */ + cached + predicate defaultTaintSanitizer(DataFlow::Node node) { + // Ignore paths through test code. + node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or + node.asExpr() instanceof ValidatedVariableAccess + } } -/** - * Holds if taint can flow in one local step from `src` to `sink` excluding - * local data flow steps. That is, `src` and `sink` are likely to represent - * different objects. - */ -predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { - localAdditionalTaintExprStep(src.asExpr(), sink.asExpr()) - or - localAdditionalTaintUpdateStep(src.asExpr(), - sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) - or - exists(Argument arg | - src.asExpr() = arg and - arg.isVararg() and - sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall() - ) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) -} - -/** - * Holds if the additional step from `src` to `sink` should be included in all - * global taint flow configurations. - */ -predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { - localAdditionalTaintStep(src, sink) or - any(AdditionalTaintStep a).step(src, sink) -} - -/** - * Holds if `node` should be a sanitizer in all global taint flow configurations - * but not in local taint. - */ -predicate defaultTaintSanitizer(DataFlow::Node node) { - // Ignore paths through test code. - node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or - node.asExpr() instanceof ValidatedVariableAccess -} +import Cached /** * Holds if taint can flow in one local step from `src` to `sink` excluding From 5eb96c1e4533729483037138d2719b729d79e521 Mon Sep 17 00:00:00 2001 From: edvraa <80588099+edvraa@users.noreply.github.com> Date: Tue, 27 Apr 2021 20:26:29 +0300 Subject: [PATCH 116/550] Remove Class cast --- java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql index ad75e993c30..3b8b5dc759a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -39,7 +39,7 @@ class RegexSink extends DataFlow::ExprNode { m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and ( ma.getArgument(1) = this.asExpr() and - m.getParameterType(1).(Class) instanceof TypeString and + m.getParameterType(1) instanceof TypeString and m.hasName([ "removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst", "replacePattern" From 7adc3c2fba0ef298fba66f767404ca55ad5ccda7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 17:25:10 +0100 Subject: [PATCH 117/550] Upload ReDoS query, qhelp and tests --- python/ql/src/Security/CWE-400/RegexDoS.qhelp | 27 ++++++ python/ql/src/Security/CWE-400/RegexDoS.ql | 82 +++++++++++++++++++ .../ql/src/Security/CWE-400/tests/re_bad.py | 22 +++++ .../ql/src/Security/CWE-400/tests/re_good.py | 22 +++++ 4 files changed, 153 insertions(+) create mode 100644 python/ql/src/Security/CWE-400/RegexDoS.qhelp create mode 100644 python/ql/src/Security/CWE-400/RegexDoS.ql create mode 100644 python/ql/src/Security/CWE-400/tests/re_bad.py create mode 100644 python/ql/src/Security/CWE-400/tests/re_good.py diff --git a/python/ql/src/Security/CWE-400/RegexDoS.qhelp b/python/ql/src/Security/CWE-400/RegexDoS.qhelp new file mode 100644 index 00000000000..de564c83347 --- /dev/null +++ b/python/ql/src/Security/CWE-400/RegexDoS.qhelp @@ -0,0 +1,27 @@ + + + + +

    If aregular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    +
    + + +

    In case user input must compose a regular expression, it should be escaped with functions such as re.escape. + + + +

  • + OWASP + Regular Expression DoS +
  • +
  • + SonarSource + RSPEC-2631 +
  • +
  • + CWE- + 400 +
  • +
    + + \ No newline at end of file diff --git a/python/ql/src/Security/CWE-400/RegexDoS.ql b/python/ql/src/Security/CWE-400/RegexDoS.ql new file mode 100644 index 00000000000..fbdca996e55 --- /dev/null +++ b/python/ql/src/Security/CWE-400/RegexDoS.ql @@ -0,0 +1,82 @@ +/** + * @name Python Regex DoS + * @description Python Regular Expression Denial of Service + * @kind path-problem + * @problem.severity error + * @id python/regex-dos + * @tags experimental + * security + * external/cwe/cwe-400 + */ + +import python +import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.internal.TaintTrackingPublic +import DataFlow::PathGraph + +class ReMethods extends string { + ReMethods() { + this = "match" or + this = "fullmatch" or + this = "search" or + this = "split" or + this = "findall" or + this = "finditer" + } +} + +class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(string reMethod, CallNode reCall | + reMethod instanceof ReMethods and + reCall = Value::named("re." + reMethod).getACall() and + this.asExpr() = reCall.getArg(0).getNode() + ) + } +} + +class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall | + patternCall = Value::named("re.compile").getACall() and + patternVar.getDefinition().getImmediateDominator() = patternCall and + patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and + reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and + this.asExpr() = patternCall.getArg(0).getNode() + ) + } +} + +class RegexDoSSink extends DataFlow::Node { + RegexDoSSink() { this instanceof DirectRegex or this instanceof CompiledRegex } +} + +class EscapeSanitizer extends DataFlow::Node { + EscapeSanitizer() { + exists(Call c | + ( + // avoid flow through any %escape% function + c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() + c.getFunc().(Name).getId().matches("%escape%") // %escape%() + ) and + this.asExpr() = c + ) + } +} + +class RegexDoSFlowConfig extends TaintTracking::Configuration { + RegexDoSFlowConfig() { this = "RegexDoSFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDoSSink } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } +} + +from RegexDoSFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ regex operation includes $@.", sink.getNode(), "This", + source.getNode(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-400/tests/re_bad.py b/python/ql/src/Security/CWE-400/tests/re_bad.py new file mode 100644 index 00000000000..6a0aa7c6614 --- /dev/null +++ b/python/ql/src/Security/CWE-400/tests/re_bad.py @@ -0,0 +1,22 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + pattern = request.args['pattern'] + + re.search(pattern, "") + + +@app.route("/compile") +def compile(): + pattern = re.compile(request.args['pattern']) + + pattern.search("") + + +# if __name__ == "__main__": +# app.run(debug=True) diff --git a/python/ql/src/Security/CWE-400/tests/re_good.py b/python/ql/src/Security/CWE-400/tests/re_good.py new file mode 100644 index 00000000000..ba09b1fe30b --- /dev/null +++ b/python/ql/src/Security/CWE-400/tests/re_good.py @@ -0,0 +1,22 @@ +from flask import request, Flask +import re + +app = Flask(__name__) + + +@app.route("/direct") +def direct(): + pattern = re.escape(request.args['pattern']) + + re.search(pattern, "") + + +@app.route("/compile") +def compile(): + pattern = re.compile(re.escape(request.args['pattern'])) + + pattern.search("") + + +# if __name__ == "__main__": +# app.run(debug=True) From bd3d2ec686ad9686180f8d6ec94d74c4703f2c1f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 19:44:23 +0100 Subject: [PATCH 118/550] Update to match consistent naming across languages --- .../{CWE-400 => CWE-730}/RegexDoS.qhelp | 4 +++ .../Security/{CWE-400 => CWE-730}/RegexDoS.ql | 30 +++++++++++-------- .../{CWE-400 => CWE-730}/tests/re_bad.py | 0 .../{CWE-400 => CWE-730}/tests/re_good.py | 0 4 files changed, 21 insertions(+), 13 deletions(-) rename python/ql/src/Security/{CWE-400 => CWE-730}/RegexDoS.qhelp (88%) rename python/ql/src/Security/{CWE-400 => CWE-730}/RegexDoS.ql (66%) rename python/ql/src/Security/{CWE-400 => CWE-730}/tests/re_bad.py (100%) rename python/ql/src/Security/{CWE-400 => CWE-730}/tests/re_good.py (100%) diff --git a/python/ql/src/Security/CWE-400/RegexDoS.qhelp b/python/ql/src/Security/CWE-730/RegexDoS.qhelp similarity index 88% rename from python/ql/src/Security/CWE-400/RegexDoS.qhelp rename to python/ql/src/Security/CWE-730/RegexDoS.qhelp index de564c83347..22f328e852b 100644 --- a/python/ql/src/Security/CWE-400/RegexDoS.qhelp +++ b/python/ql/src/Security/CWE-730/RegexDoS.qhelp @@ -22,6 +22,10 @@ CWE- 400 +
  • + CWE- + 730 +
  • \ No newline at end of file diff --git a/python/ql/src/Security/CWE-400/RegexDoS.ql b/python/ql/src/Security/CWE-730/RegexDoS.ql similarity index 66% rename from python/ql/src/Security/CWE-400/RegexDoS.ql rename to python/ql/src/Security/CWE-730/RegexDoS.ql index fbdca996e55..79905fe6921 100644 --- a/python/ql/src/Security/CWE-400/RegexDoS.ql +++ b/python/ql/src/Security/CWE-730/RegexDoS.ql @@ -1,14 +1,17 @@ /** - * @name Python Regex DoS - * @description Python Regular Expression Denial of Service + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being escaped, + * otherwise a malicious user may be able to inject an expression that could require + * exponential time on certain inputs. * @kind path-problem * @problem.severity error - * @id python/regex-dos - * @tags experimental - * security + * @id python/regex-injection + * @tags security + * external/cwe/cwe-730 * external/cwe/cwe-400 */ +// determine precision above import python import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.DataFlow @@ -16,6 +19,7 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.internal.TaintTrackingPublic import DataFlow::PathGraph +// Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { ReMethods() { this = "match" or @@ -49,8 +53,8 @@ class CompiledRegex extends DataFlow::Node { } } -class RegexDoSSink extends DataFlow::Node { - RegexDoSSink() { this instanceof DirectRegex or this instanceof CompiledRegex } +class RegexInjectionSink extends DataFlow::Node { + RegexInjectionSink() { this instanceof DirectRegex or this instanceof CompiledRegex } } class EscapeSanitizer extends DataFlow::Node { @@ -66,17 +70,17 @@ class EscapeSanitizer extends DataFlow::Node { } } -class RegexDoSFlowConfig extends TaintTracking::Configuration { - RegexDoSFlowConfig() { this = "RegexDoSFlowConfig" } +class RegexInjectionFlowConfig extends TaintTracking::Configuration { + RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDoSSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } } -from RegexDoSFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regex operation includes $@.", sink.getNode(), "This", - source.getNode(), "a user-provided value" +select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/Security/CWE-400/tests/re_bad.py b/python/ql/src/Security/CWE-730/tests/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-400/tests/re_bad.py rename to python/ql/src/Security/CWE-730/tests/re_bad.py diff --git a/python/ql/src/Security/CWE-400/tests/re_good.py b/python/ql/src/Security/CWE-730/tests/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-400/tests/re_good.py rename to python/ql/src/Security/CWE-730/tests/re_good.py From afc4f51e9c29bfef762f372b732157d583bdaeac Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:10:18 +0100 Subject: [PATCH 119/550] Remove CWE references --- python/ql/src/Security/CWE-730/RegexDoS.qhelp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/python/ql/src/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/Security/CWE-730/RegexDoS.qhelp index 22f328e852b..002cbcef0a8 100644 --- a/python/ql/src/Security/CWE-730/RegexDoS.qhelp +++ b/python/ql/src/Security/CWE-730/RegexDoS.qhelp @@ -18,14 +18,6 @@ SonarSource RSPEC-2631 -
  • - CWE- - 400 -
  • -
  • - CWE- - 730 -
  • \ No newline at end of file From 21f8135fa6824f42bdf643d48c34b1a712cbe715 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 20:19:08 +0100 Subject: [PATCH 120/550] Move to experimental folder --- python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.qhelp | 0 python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.ql | 0 python/ql/src/{ => experimental}/Security/CWE-730/tests/re_bad.py | 0 .../ql/src/{ => experimental}/Security/CWE-730/tests/re_good.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.qhelp (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexDoS.ql (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/tests/re_bad.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/tests/re_good.py (100%) diff --git a/python/ql/src/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp similarity index 100% rename from python/ql/src/Security/CWE-730/RegexDoS.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql similarity index 100% rename from python/ql/src/Security/CWE-730/RegexDoS.ql rename to python/ql/src/experimental/Security/CWE-730/RegexDoS.ql diff --git a/python/ql/src/Security/CWE-730/tests/re_bad.py b/python/ql/src/experimental/Security/CWE-730/tests/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-730/tests/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/tests/re_bad.py diff --git a/python/ql/src/Security/CWE-730/tests/re_good.py b/python/ql/src/experimental/Security/CWE-730/tests/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-730/tests/re_good.py rename to python/ql/src/experimental/Security/CWE-730/tests/re_good.py From 6cc714464c519dd59bbc4456f312ca6b1b10a984 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Thu, 18 Mar 2021 22:42:49 +0100 Subject: [PATCH 121/550] Apply suggestions from code review Co-authored-by: yoff --- .../experimental/Security/CWE-730/RegexDoS.ql | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql index 79905fe6921..b89835aad8a 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql @@ -21,34 +21,25 @@ import DataFlow::PathGraph // Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { - ReMethods() { - this = "match" or - this = "fullmatch" or - this = "search" or - this = "split" or - this = "findall" or - this = "finditer" - } + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }``` } class DirectRegex extends DataFlow::Node { DirectRegex() { - exists(string reMethod, CallNode reCall | - reMethod instanceof ReMethods and - reCall = Value::named("re." + reMethod).getACall() and - this.asExpr() = reCall.getArg(0).getNode() + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) ) } } class CompiledRegex extends DataFlow::Node { CompiledRegex() { - exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall | - patternCall = Value::named("re.compile").getACall() and - patternVar.getDefinition().getImmediateDominator() = patternCall and - patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and - reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and - this.asExpr() = patternCall.getArg(0).getNode() + exists(DataFlow::CallCfgNode patternCall, AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) ) } } From 63f708dd57e53bd3f4cb0c73aa865384933a1789 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:03:33 +0100 Subject: [PATCH 122/550] Apply suggestions --- python/ql/src/experimental/Security/CWE-730/RegexDoS.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql index b89835aad8a..d35b596c045 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql @@ -17,11 +17,12 @@ import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.internal.TaintTrackingPublic +import semmle.python.ApiGraphs import DataFlow::PathGraph // Should this be moved to a different structure? (For other queries to be able to use it) class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }``` + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } } class DirectRegex extends DataFlow::Node { @@ -35,7 +36,7 @@ class DirectRegex extends DataFlow::Node { class CompiledRegex extends DataFlow::Node { CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, AttrRead reMethod | + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and From 5dae9207831dbe30365235ef03e9c28357172668 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:04:47 +0100 Subject: [PATCH 123/550] Edit filenames to match consistent naming --- .../Security/CWE-730/{RegexDoS.qhelp => RegexInjection.qhelp} | 0 .../Security/CWE-730/{RegexDoS.ql => RegexInjection.ql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/src/experimental/Security/CWE-730/{RegexDoS.qhelp => RegexInjection.qhelp} (100%) rename python/ql/src/experimental/Security/CWE-730/{RegexDoS.ql => RegexInjection.ql} (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexDoS.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/experimental/Security/CWE-730/RegexDoS.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexDoS.ql rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.ql From f45307f9906e581a527710eb5b6a1bf224a36c56 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:33:10 +0100 Subject: [PATCH 124/550] Apply rebase --- .../Security/CWE-730/RegexInjection.qhelp | 0 .../ql/src/Security/CWE-730/RegexInjection.ql | 22 ++++++ .../tests => Security/CWE-730}/re_bad.py | 0 .../tests => Security/CWE-730}/re_good.py | 0 .../Security/CWE-730/RegexInjection.ql | 78 ------------------- python/ql/src/semmle/python/Concepts.qll | 43 ++++++++++ .../security/dataflow/RegexInjection.qll | 23 ++++++ 7 files changed, 88 insertions(+), 78 deletions(-) rename python/ql/src/{experimental => }/Security/CWE-730/RegexInjection.qhelp (100%) create mode 100644 python/ql/src/Security/CWE-730/RegexInjection.ql rename python/ql/src/{experimental/Security/CWE-730/tests => Security/CWE-730}/re_bad.py (100%) rename python/ql/src/{experimental/Security/CWE-730/tests => Security/CWE-730}/re_good.py (100%) delete mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.ql create mode 100644 python/ql/src/semmle/python/security/dataflow/RegexInjection.qll diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp rename to python/ql/src/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/Security/CWE-730/RegexInjection.ql new file mode 100644 index 00000000000..27bbe69529d --- /dev/null +++ b/python/ql/src/Security/CWE-730/RegexInjection.ql @@ -0,0 +1,22 @@ +/** + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being escaped, + * otherwise a malicious user may be able to inject an expression that could require + * exponential time on certain inputs. + * @kind path-problem + * @problem.severity error + * @id python/regex-injection + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +// determine precision above +import python +import semmle.python.security.dataflow.RegexInjection +import DataFlow::PathGraph + +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/experimental/Security/CWE-730/tests/re_bad.py b/python/ql/src/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/tests/re_bad.py rename to python/ql/src/Security/CWE-730/re_bad.py diff --git a/python/ql/src/experimental/Security/CWE-730/tests/re_good.py b/python/ql/src/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/tests/re_good.py rename to python/ql/src/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql deleted file mode 100644 index d35b596c045..00000000000 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @name Regular expression injection - * @description User input should not be used in regular expressions without first being escaped, - * otherwise a malicious user may be able to inject an expression that could require - * exponential time on certain inputs. - * @kind path-problem - * @problem.severity error - * @id python/regex-injection - * @tags security - * external/cwe/cwe-730 - * external/cwe/cwe-400 - */ - -// determine precision above -import python -import semmle.python.dataflow.new.RemoteFlowSources -import semmle.python.dataflow.new.DataFlow -import semmle.python.dataflow.new.TaintTracking -import semmle.python.dataflow.new.internal.TaintTrackingPublic -import semmle.python.ApiGraphs -import DataFlow::PathGraph - -// Should this be moved to a different structure? (For other queries to be able to use it) -class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } -} - -class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) - } -} - -class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) - } -} - -class RegexInjectionSink extends DataFlow::Node { - RegexInjectionSink() { this instanceof DirectRegex or this instanceof CompiledRegex } -} - -class EscapeSanitizer extends DataFlow::Node { - EscapeSanitizer() { - exists(Call c | - ( - // avoid flow through any %escape% function - c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() - c.getFunc().(Name).getId().matches("%escape%") // %escape%() - ) and - this.asExpr() = c - ) - } -} - -class RegexInjectionFlowConfig extends TaintTracking::Configuration { - RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } - - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer } -} - -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 4be00ba5bf9..f58f6317e1e 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Frameworks +private import semmle.python.ApiGraphs /** * A data-flow node that executes an operating system command, @@ -625,5 +626,47 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } +/* + */ + +class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } +} + +class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } +} + +class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } +} + +class RegexExecution extends DataFlow::Node { + RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } +} + +// pending refactor if needed +class RegexEscape extends DataFlow::Node { + RegexEscape() { + exists(Call c | + ( + // avoid flow through any %escape% function + c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() + c.getFunc().(Name).getId().matches("%escape%") // %escape%() + ) and + this.asExpr() = c + ) } } diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll new file mode 100644 index 00000000000..5a8f386a781 --- /dev/null +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -0,0 +1,23 @@ +/** + * Provides a taint-tracking configuration for detecting regular expression injections + * vulnerabilities. + */ + +import python +import semmle.python.Concepts +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking +import semmle.python.dataflow.new.RemoteFlowSources + +/** + * A taint-tracking configuration for detecting regular expression injections. + */ +class RegexInjectionFlowConfig extends TaintTracking::Configuration { + RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexExecution } + + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } +} From e4736d064e525ca51cdb52df1fedcdb54b0b8b04 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 18 Mar 2021 23:36:54 +0100 Subject: [PATCH 125/550] Typo --- .../ql/src/semmle/python/security/dataflow/RegexInjection.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 5a8f386a781..968010dd9a0 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -1,5 +1,5 @@ /** - * Provides a taint-tracking configuration for detecting regular expression injections + * Provides a taint-tracking configuration for detecting regular expression injection * vulnerabilities. */ From a1b5cc3bc61860e50449617a8c79a9bfa975b3bd Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:26:21 +0100 Subject: [PATCH 126/550] Typo --- python/ql/src/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/Security/CWE-730/RegexInjection.qhelp index 002cbcef0a8..2e3d238daa4 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/Security/CWE-730/RegexInjection.qhelp @@ -2,7 +2,7 @@ -

    If aregular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    +

    If a regular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    From 6d5a0f2f842b1f7a459a2e8e5d54c67fc86516c7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:34:13 +0100 Subject: [PATCH 127/550] Limit Sanitizer to re.escape(arg) --- python/ql/src/semmle/python/Concepts.qll | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index f58f6317e1e..60880794845 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -657,16 +657,8 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } } -// pending refactor if needed class RegexEscape extends DataFlow::Node { RegexEscape() { - exists(Call c | - ( - // avoid flow through any %escape% function - c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%() - c.getFunc().(Name).getId().matches("%escape%") // %escape%() - ) and - this.asExpr() = c - ) + this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) } } From caaf5436c6a4ed8a32c0b4f43a1418052a4b7ba5 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 01:51:59 +0100 Subject: [PATCH 128/550] Attempt to restructuring ReMethods and RegexExecution's modules --- python/ql/src/semmle/python/Concepts.qll | 2 +- .../src/semmle/python/frameworks/Stdlib.qll | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 60880794845..751f9f0a302 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -654,7 +654,7 @@ class CompiledRegex extends DataFlow::Node { } class RegexExecution extends DataFlow::Node { - RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } + RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? } class RegexEscape extends DataFlow::Node { diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index 01df130d0db..c074e6fe1cd 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -864,6 +864,36 @@ private module Stdlib { class Sqlite3 extends PEP249ModuleApiNode { Sqlite3() { this = API::moduleImport("sqlite3") } } + + // --------------------------------------------------------------------------- + // re + // --------------------------------------------------------------------------- + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } + } + + /** re.ReMethod(pattern, string) */ + private class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } + } + + /** re.compile(pattern).ReMethod */ + class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } + } } // --------------------------------------------------------------------------- From 3daec8e6a27a0776220ca52fe28e184717e1fc50 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 17:46:32 +0100 Subject: [PATCH 129/550] Enclose Sinks and ReMethods in a module --- .../src/semmle/python/frameworks/Stdlib.qll | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c074e6fe1cd..f9949560d68 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -868,30 +868,32 @@ private module Stdlib { // --------------------------------------------------------------------------- // re // --------------------------------------------------------------------------- - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } - } - - /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) + private module Re { + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } } - } - /** re.compile(pattern).ReMethod */ - class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) + /** re.ReMethod(pattern, string) */ + private class DirectRegex extends DataFlow::Node { + DirectRegex() { + exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | + reCall = API::moduleImport("re").getMember(reMethod).getACall() and + this = reCall.getArg(0) + ) + } + } + + /** re.compile(pattern).ReMethod */ + class CompiledRegex extends DataFlow::Node { + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and + this = patternCall.getArg(0) + ) + } } } } From b207929e0ad280fec73fffb7d4c5d193e01f3e34 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 19 Mar 2021 18:07:39 +0100 Subject: [PATCH 130/550] RegexExecution restructuring --- python/ql/src/semmle/python/Concepts.qll | 18 +++++++++++++++--- .../ql/src/semmle/python/frameworks/Stdlib.qll | 9 ++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 751f9f0a302..aa0c748c1da 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -656,9 +656,21 @@ class CompiledRegex extends DataFlow::Node { class RegexExecution extends DataFlow::Node { RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? } +/* + */ -class RegexEscape extends DataFlow::Node { - RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) +module RegexExecution { + abstract class Range extends DataFlow::Node { + DataFlow::Node getRegexNode() { + result instanceof DirectRegex or result instanceof CompiledRegex + } } } + +class RegexExecution extends DataFlow::Node { + override RegexExecution::Range range; + + RegexExecution() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } +} diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index f9949560d68..ed5a56be931 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -885,7 +885,7 @@ private module Stdlib { } /** re.compile(pattern).ReMethod */ - class CompiledRegex extends DataFlow::Node { + private class CompiledRegex extends DataFlow::Node { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and @@ -895,6 +895,13 @@ private module Stdlib { ) } } + + private class RegexEscape extends Concepts::RegexExecution { + RegexEscape() { + this = + API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } + } } } From 249e4097e3a2faaa7b10914ba01b99a39d2c8f65 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Mon, 22 Mar 2021 15:12:12 +0100 Subject: [PATCH 131/550] Change query ID Co-authored-by: Rasmus Wriedt Larsen --- python/ql/src/Security/CWE-730/RegexInjection.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/Security/CWE-730/RegexInjection.ql index 27bbe69529d..3baa3b54bea 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/Security/CWE-730/RegexInjection.ql @@ -5,7 +5,7 @@ * exponential time on certain inputs. * @kind path-problem * @problem.severity error - * @id python/regex-injection + * @id py/regex-injection * @tags security * external/cwe/cwe-730 * external/cwe/cwe-400 From b27b77c38fda588586bd1ea3a0e942a5f4e403f8 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Mon, 22 Mar 2021 15:30:54 +0100 Subject: [PATCH 132/550] Apply suggestions from code review Co-authored-by: yoff --- python/ql/src/semmle/python/Concepts.qll | 4 +--- python/ql/src/semmle/python/frameworks/Stdlib.qll | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index aa0c748c1da..497ea0c0e31 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -661,9 +661,7 @@ class RegexExecution extends DataFlow::Node { module RegexExecution { abstract class Range extends DataFlow::Node { - DataFlow::Node getRegexNode() { - result instanceof DirectRegex or result instanceof CompiledRegex - } + abstract DataFlow::Node getRegexNode(); } } diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index ed5a56be931..c40ac53f8e4 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -875,7 +875,7 @@ private module Stdlib { } /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::Node { + private class DirectRegex extends RegexExecution::Range { DirectRegex() { exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | reCall = API::moduleImport("re").getMember(reMethod).getACall() and @@ -885,7 +885,7 @@ private module Stdlib { } /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends DataFlow::Node { + private class CompiledRegex extends RegexExecution::Range { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | patternCall = API::moduleImport("re").getMember("compile").getACall() and @@ -896,7 +896,7 @@ private module Stdlib { } } - private class RegexEscape extends Concepts::RegexExecution { + private class RegexEscape extends DataFlow::Node { RegexEscape() { this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) From 0f20eeb395645b2185528b832a6f56ba039b0552 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Wed, 24 Mar 2021 09:34:04 +0100 Subject: [PATCH 133/550] Apply suggestions Co-authored-by: yoff --- python/ql/src/semmle/python/Concepts.qll | 2 +- .../src/semmle/python/frameworks/Stdlib.qll | 21 ++++++++++++------- .../security/dataflow/RegexInjection.qll | 7 +++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 497ea0c0e31..d2defca27a1 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -666,7 +666,7 @@ module RegexExecution { } class RegexExecution extends DataFlow::Node { - override RegexExecution::Range range; + RegexExecution::Range range; RegexExecution() { this = range } diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c40ac53f8e4..c86e4cf2e22 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -875,25 +875,32 @@ private module Stdlib { } /** re.ReMethod(pattern, string) */ - private class DirectRegex extends RegexExecution::Range { + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) + this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + regexNode = this.getArg(0) } + + override DataFlow::Node getRegexNode() { result = regexNode } } /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends RegexExecution::Range { + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) + regexNode = patternCall.getArg(0) ) } + + override DataFlow::Node getRegexNode() { result = regexNode } } private class RegexEscape extends DataFlow::Node { diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 968010dd9a0..2758db37d65 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -17,7 +17,10 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof RegexExecution } + override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = + API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } } From 444a15a461ee8f99428650daa2ae6d95f7eb25b1 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 10:00:10 +0100 Subject: [PATCH 134/550] Polish imports --- python/ql/src/semmle/python/Concepts.qll | 1 - python/ql/src/semmle/python/security/dataflow/RegexInjection.qll | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index d2defca27a1..cc2e4ac3ca7 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import semmle.python.Frameworks -private import semmle.python.ApiGraphs /** * A data-flow node that executes an operating system command, diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll index 2758db37d65..98545816c82 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll @@ -8,6 +8,7 @@ import semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.ApiGraphs /** * A taint-tracking configuration for detecting regular expression injections. From 28fdeba4fafc4ab0b7cff4cb1307fff3298440b8 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 17:59:48 +0100 Subject: [PATCH 135/550] Structure development --- .../Security/CWE-730/RegexInjection.qhelp | 0 .../Security/CWE-730/RegexInjection.ql | 4 +- .../Security/CWE-730/re_bad.py | 0 .../Security/CWE-730/re_good.py | 0 .../experimental/semmle/python/Concepts.qll | 29 +++ .../semmle/python/frameworks/Stdlib.qll | 232 +++++++++++++++++- .../security/injection}/RegexInjection.qll | 2 +- python/ql/src/semmle/python/Concepts.qll | 46 +--- .../src/semmle/python/frameworks/Stdlib.qll | 46 ---- 9 files changed, 264 insertions(+), 95 deletions(-) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexInjection.qhelp (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/RegexInjection.ql (92%) rename python/ql/src/{ => experimental}/Security/CWE-730/re_bad.py (100%) rename python/ql/src/{ => experimental}/Security/CWE-730/re_good.py (100%) rename python/ql/src/{semmle/python/security/dataflow => experimental/semmle/python/security/injection}/RegexInjection.qll (95%) diff --git a/python/ql/src/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp similarity index 100% rename from python/ql/src/Security/CWE-730/RegexInjection.qhelp rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp diff --git a/python/ql/src/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql similarity index 92% rename from python/ql/src/Security/CWE-730/RegexInjection.ql rename to python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 3baa3b54bea..ccb97fbee54 100644 --- a/python/ql/src/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -13,10 +13,10 @@ // determine precision above import python -import semmle.python.security.dataflow.RegexInjection +import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" + sink.getNode(), "This", source.getNode(), "user-provided value" \ No newline at end of file diff --git a/python/ql/src/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/Security/CWE-730/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/re_bad.py diff --git a/python/ql/src/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/Security/CWE-730/re_good.py rename to python/ql/src/experimental/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 904b7967ee8..172a7fc2003 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,3 +13,32 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks + +/** Provides classes for modeling Regular Expression-related APIs. */ +module RegexExecution { + /** + * A data-flow node that works with regular expressions. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RegexExecution` instead. + */ + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getRegexNode(); + abstract Attribute getRegexMethod(); + } +} + +/** + * A data-flow node that works with regular expressions. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexExecution::Range` instead. + */ +class RegexExecution extends DataFlow::Node { + RegexExecution::Range range; + + RegexExecution() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } + Attribute getRegexMethod() { result = range.getRegexMethod() } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 420caf0d73b..edaa42fad8a 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -8,4 +8,234 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts -private import semmle.python.ApiGraphs + +/** Provides models for the Python standard library. */ +private module Stdlib { + // --------------------------------------------------------------------------- + // re + // --------------------------------------------------------------------------- + private module Re { + + /** Gets a reference to the `re` module. */ + private DataFlow::Node re(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importNode("re") + or + exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t)) + } + + /** Gets a reference to the `re` module. */ + DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) } + + /** + * Gets a reference to the attribute `attr_name` of the `re` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile"] and + ( + t.start() and + result = DataFlow::importNode("re" + "." + attr_name) + or + t.startInAttr(attr_name) and + result = re() + ) + or + // Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + re_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate re_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary) + } + + /** + * Gets a reference to the attribute `attr_name` of the `re` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_attr(string attr_name) { + result = re_attr(DataFlow::TypeTracker::end(), attr_name) + } + + /** + * Gets a reference to any `attr_name` of the `re` module that immediately executes an expression. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node re_exec_attr() { + exists(string attr_name | + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and + result = re_attr(DataFlow::TypeTracker::end(), attr_name) + ) + } + + /** + * A call to `re.match` + * See https://docs.python.org/3/library/re.html#re.match + */ + private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.fullmatch` + * See https://docs.python.org/3/library/re.html#re.fullmatch + */ + private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.search` + * See https://docs.python.org/3/library/re.html#re.search + */ + private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.split` + * See https://docs.python.org/3/library/re.html#re.split + */ + private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.findall` + * See https://docs.python.org/3/library/re.html#re.findall + */ + private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.finditer` + * See https://docs.python.org/3/library/re.html#re.finditer + */ + private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.sub` + * See https://docs.python.org/3/library/re.html#re.sub + */ + private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.subn` + * See https://docs.python.org/3/library/re.html#re.subn + */ + private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + + /** + * A call to `re.compile` + * See https://docs.python.org/3/library/re.html#re.match + */ + private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { + exists (DataFlow::AttrRead reMethod | + reMethod = re_exec_attr() and + node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and + result = reMethod.asExpr().(Attribute) + ) + } + } + + /** + * A class for modeling expressions immediately executing a regular expression. + * See `re_exec_attr()` + */ + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + DirectRegex() { + // needs inheritance (?) + this = re_exec_attr() and regexNode = this.getRegexNode() and regexMethod = this.getRegexMethod() + } + + override DataFlow::Node getRegexNode() { result = regexNode } + override Attribute getRegexMethod() { result = regexMethod } + } + + /** + * A class for finding `ReCompileCall` whose `Attribute` is an instance of `DirectRegex`. + * See `ReCompileCall`, `DirectRegex`, `re_exec_attr()` + */ + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + CompiledRegex() { + exists(DirectRegex reMethod, ReCompileCall compileCall | + this = reMethod and + reMethod.getRegexMethod() = compileCall.getRegexMethod() and + regexNode = compileCall.getRegexNode() and + regexMethod = reMethod.getRegexMethod() + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + override Attribute getRegexMethod() { result = regexMethod } + } + } +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll similarity index 95% rename from python/ql/src/semmle/python/security/dataflow/RegexInjection.qll rename to python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 98545816c82..5e2cc684b43 100644 --- a/python/ql/src/semmle/python/security/dataflow/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -4,7 +4,7 @@ */ import python -import semmle.python.Concepts +import experimental.semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index cc2e4ac3ca7..ccae7251a2c 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,49 +625,5 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } -/* - */ - -class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } -} - -class DirectRegex extends DataFlow::Node { - DirectRegex() { - exists(ReMethods reMethod, DataFlow::CallCfgNode reCall | - reCall = API::moduleImport("re").getMember(reMethod).getACall() and - this = reCall.getArg(0) - ) } -} - -class CompiledRegex extends DataFlow::Node { - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - this = patternCall.getArg(0) - ) - } -} - -class RegexExecution extends DataFlow::Node { - RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib? -} -/* - */ - -module RegexExecution { - abstract class Range extends DataFlow::Node { - abstract DataFlow::Node getRegexNode(); - } -} - -class RegexExecution extends DataFlow::Node { - RegexExecution::Range range; - - RegexExecution() { this = range } - - DataFlow::Node getRegexNode() { result = range.getRegexNode() } -} +} \ No newline at end of file diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index c86e4cf2e22..01df130d0db 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -864,52 +864,6 @@ private module Stdlib { class Sqlite3 extends PEP249ModuleApiNode { Sqlite3() { this = API::moduleImport("sqlite3") } } - - // --------------------------------------------------------------------------- - // re - // --------------------------------------------------------------------------- - private module Re { - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] } - } - - /** re.ReMethod(pattern, string) */ - private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - - DirectRegex() { - this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and - regexNode = this.getArg(0) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - } - - /** re.compile(pattern).ReMethod */ - private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - - CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - this.getFunction() = reMethod and - patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and - regexNode = patternCall.getArg(0) - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - } - - private class RegexEscape extends DataFlow::Node { - RegexEscape() { - this = - API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) - } - } - } } // --------------------------------------------------------------------------- From a1a3c98d92fa433d642139a3e259713a31cc2c3e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 18:16:36 +0100 Subject: [PATCH 136/550] Undo main Concepts.qll change --- python/ql/src/semmle/python/Concepts.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index ccae7251a2c..2706a497f9a 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,5 +625,4 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } - } -} \ No newline at end of file + } \ No newline at end of file From d61adccd3c6b6511e106b106bfe2d6efef612c14 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 18:23:59 +0100 Subject: [PATCH 137/550] Take main Concepts.qll out of the PR --- python/ql/src/semmle/python/Concepts.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/Concepts.qll b/python/ql/src/semmle/python/Concepts.qll index 2706a497f9a..4be00ba5bf9 100644 --- a/python/ql/src/semmle/python/Concepts.qll +++ b/python/ql/src/semmle/python/Concepts.qll @@ -625,4 +625,5 @@ module Cryptography { final override int minimumSecureKeySize() { result = 224 } } } - } \ No newline at end of file + } +} From b5ea41fcca6d80ddeaa7209a8f7f0edaef5237f8 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 21:02:09 +0100 Subject: [PATCH 138/550] Fix CompiledRegex --- .../src/experimental/semmle/python/frameworks/Stdlib.qll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index edaa42fad8a..a6d848cc126 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -226,11 +226,10 @@ private module Stdlib { Attribute regexMethod; CompiledRegex() { - exists(DirectRegex reMethod, ReCompileCall compileCall | - this = reMethod and - reMethod.getRegexMethod() = compileCall.getRegexMethod() and + exists(ReCompileCall compileCall | regexNode = compileCall.getRegexNode() and - regexMethod = reMethod.getRegexMethod() + regexMethod = compileCall.getRegexMethod() and + this = compileCall ) } From ce23db2e9ca80497f71b7c9792f60582906db42b Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 24 Mar 2021 21:11:45 +0100 Subject: [PATCH 139/550] Move Sanitizer to ReEscapeCall --- .../semmle/python/frameworks/Stdlib.qll | 15 ++++++++++++++- .../python/security/injection/RegexInjection.qll | 8 +++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a6d848cc126..d243eacd3d0 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -32,7 +32,7 @@ private module Stdlib { * WARNING: Only holds for a few predefined attributes. */ private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile"] and + attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", "escape"] and ( t.start() and result = DataFlow::importNode("re" + "." + attr_name) @@ -181,6 +181,19 @@ private module Stdlib { override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } + /** + * A call to `re.escape` + * See https://docs.python.org/3/library/re.html#re.escape + */ + private class ReEscapeCall extends RegexExecution::Range, DataFlow::CfgNode { + override CallNode node; + + ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } + + override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } + } + /** * A call to `re.compile` * See https://docs.python.org/3/library/re.html#re.match diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 5e2cc684b43..0d42acbac49 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -5,6 +5,7 @@ import python import experimental.semmle.python.Concepts +import experimental.semmle.python.frameworks.Stdlib import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources @@ -18,10 +19,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } + override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = - API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) - } + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof ReEscapeCall } } From ee1d2b645b0a42a370c6c47fb721ecff3562b348 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 25 Mar 2021 18:19:11 +0100 Subject: [PATCH 140/550] Delete DirectRegex and CompiledRegex --- .../semmle/python/frameworks/Stdlib.qll | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index d243eacd3d0..fab8debb1b8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -212,42 +212,5 @@ private module Stdlib { ) } } - - /** - * A class for modeling expressions immediately executing a regular expression. - * See `re_exec_attr()` - */ - private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - Attribute regexMethod; - - DirectRegex() { - // needs inheritance (?) - this = re_exec_attr() and regexNode = this.getRegexNode() and regexMethod = this.getRegexMethod() - } - - override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } - } - - /** - * A class for finding `ReCompileCall` whose `Attribute` is an instance of `DirectRegex`. - * See `ReCompileCall`, `DirectRegex`, `re_exec_attr()` - */ - private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { - DataFlow::Node regexNode; - Attribute regexMethod; - - CompiledRegex() { - exists(ReCompileCall compileCall | - regexNode = compileCall.getRegexNode() and - regexMethod = compileCall.getRegexMethod() and - this = compileCall - ) - } - - override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } - } } } \ No newline at end of file From 30554a16da55ef9d652ad60e6437593682b07c23 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 25 Mar 2021 18:20:13 +0100 Subject: [PATCH 141/550] Format --- .../Security/CWE-730/RegexInjection.ql | 2 +- .../semmle/python/frameworks/Stdlib.qll | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index ccb97fbee54..9d3da12f9bd 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -19,4 +19,4 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" \ No newline at end of file + sink.getNode(), "This", source.getNode(), "user-provided value" diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index fab8debb1b8..4b5f8f63f6a 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -15,7 +15,6 @@ private module Stdlib { // re // --------------------------------------------------------------------------- private module Re { - /** Gets a reference to the `re` module. */ private DataFlow::Node re(DataFlow::TypeTracker t) { t.start() and @@ -32,7 +31,10 @@ private module Stdlib { * WARNING: Only holds for a few predefined attributes. */ private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", "escape"] and + attr_name in [ + "match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", + "escape" + ] and ( t.start() and result = DataFlow::importNode("re" + "." + attr_name) @@ -87,6 +89,7 @@ private module Stdlib { ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -100,6 +103,7 @@ private module Stdlib { ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -113,6 +117,7 @@ private module Stdlib { ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -126,6 +131,7 @@ private module Stdlib { ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -139,6 +145,7 @@ private module Stdlib { ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -152,6 +159,7 @@ private module Stdlib { ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -165,6 +173,7 @@ private module Stdlib { ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -178,6 +187,7 @@ private module Stdlib { ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -191,6 +201,7 @@ private module Stdlib { ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } + override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } } @@ -204,13 +215,14 @@ private module Stdlib { ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - override Attribute getRegexMethod() { - exists (DataFlow::AttrRead reMethod | + + override Attribute getRegexMethod() { + exists(DataFlow::AttrRead reMethod | reMethod = re_exec_attr() and node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and result = reMethod.asExpr().(Attribute) ) - } + } } } -} \ No newline at end of file +} From 3d990c595097c4af80392cc0ac26988605d2f059 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:05:50 +0100 Subject: [PATCH 142/550] Get back to ApiGraphs --- .../experimental/semmle/python/Concepts.qll | 9 + .../semmle/python/frameworks/Stdlib.qll | 254 +++--------------- .../security/injection/RegexInjection.qll | 3 +- 3 files changed, 51 insertions(+), 215 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 172a7fc2003..8a88ffb2154 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,6 +13,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks +private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { @@ -24,6 +25,7 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); + abstract Attribute getRegexMethod(); } } @@ -40,5 +42,12 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } + Attribute getRegexMethod() { result = range.getRegexMethod() } } + +class RegexEscape extends DataFlow::Node { + RegexEscape() { + this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 4b5f8f63f6a..5a3441bed83 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -8,221 +8,49 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs /** Provides models for the Python standard library. */ -private module Stdlib { - // --------------------------------------------------------------------------- - // re - // --------------------------------------------------------------------------- - private module Re { - /** Gets a reference to the `re` module. */ - private DataFlow::Node re(DataFlow::TypeTracker t) { - t.start() and - result = DataFlow::importNode("re") - or - exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t)) - } - - /** Gets a reference to the `re` module. */ - DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) } - - /** - * Gets a reference to the attribute `attr_name` of the `re` module. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in [ - "match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile", - "escape" - ] and - ( - t.start() and - result = DataFlow::importNode("re" + "." + attr_name) - or - t.startInAttr(attr_name) and - result = re() - ) - or - // Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)` - // we have inlined that code and forced a join - exists(DataFlow::TypeTracker t2 | - exists(DataFlow::StepSummary summary | - re_attr_first_join(t2, attr_name, result, summary) and - t = t2.append(summary) - ) - ) - } - - pragma[nomagic] - private predicate re_attr_first_join( - DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary - ) { - DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary) - } - - /** - * Gets a reference to the attribute `attr_name` of the `re` module. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_attr(string attr_name) { - result = re_attr(DataFlow::TypeTracker::end(), attr_name) - } - - /** - * Gets a reference to any `attr_name` of the `re` module that immediately executes an expression. - * WARNING: Only holds for a few predefined attributes. - */ - private DataFlow::Node re_exec_attr() { - exists(string attr_name | - attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and - result = re_attr(DataFlow::TypeTracker::end(), attr_name) - ) - } - - /** - * A call to `re.match` - * See https://docs.python.org/3/library/re.html#re.match - */ - private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.fullmatch` - * See https://docs.python.org/3/library/re.html#re.fullmatch - */ - private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.search` - * See https://docs.python.org/3/library/re.html#re.search - */ - private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.split` - * See https://docs.python.org/3/library/re.html#re.split - */ - private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.findall` - * See https://docs.python.org/3/library/re.html#re.findall - */ - private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.finditer` - * See https://docs.python.org/3/library/re.html#re.finditer - */ - private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.sub` - * See https://docs.python.org/3/library/re.html#re.sub - */ - private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.subn` - * See https://docs.python.org/3/library/re.html#re.subn - */ - private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.escape` - * See https://docs.python.org/3/library/re.html#re.escape - */ - private class ReEscapeCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) } - } - - /** - * A call to `re.compile` - * See https://docs.python.org/3/library/re.html#re.match - */ - private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode { - override CallNode node; - - ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() } - - override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) } - - override Attribute getRegexMethod() { - exists(DataFlow::AttrRead reMethod | - reMethod = re_exec_attr() and - node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and - result = reMethod.asExpr().(Attribute) - ) - } +private module Re { + /** List of re methods. */ + private class ReMethods extends string { + ReMethods() { + this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] } } + + private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + DirectRegex() { + this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + regexNode = this.getArg(0) and + regexMethod = this.asExpr().(Attribute) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getRegexMethod() { result = regexMethod } + } + + private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { + DataFlow::Node regexNode; + Attribute regexMethod; + + CompiledRegex() { + exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | + this = reMethod and + patternCall = API::moduleImport("re").getMember("compile").getACall() and + patternCall = reMethod.(DataFlow::AttrRead).getObject().getALocalSource() and + regexNode = patternCall.getArg(0) and + // regexMethod is *not* worked out outside class instanciation because `CompiledRegex` focuses on re.compile(pattern).ReMethod + regexMethod = reMethod.getRegexMethod() + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getRegexMethod() { result = regexMethod } + } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 0d42acbac49..102fb80cfff 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -5,7 +5,6 @@ import python import experimental.semmle.python.Concepts -import experimental.semmle.python.frameworks.Stdlib import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources @@ -21,5 +20,5 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof ReEscapeCall } + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } } From 805f86a5cf49a81fa47c616064fc88efa12aca12 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:15:12 +0100 Subject: [PATCH 143/550] Polish RegexEscape --- .../ql/src/experimental/semmle/python/Concepts.qll | 13 +++++++++++-- .../python/security/injection/RegexInjection.qll | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8a88ffb2154..b8cef8e4e11 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -46,8 +46,17 @@ class RegexExecution extends DataFlow::Node { Attribute getRegexMethod() { result = range.getRegexMethod() } } -class RegexEscape extends DataFlow::Node { +class RegexEscape extends DataFlow::CallCfgNode { + DataFlow::Node regexNode; + Attribute regexMethod; + RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0) + this = API::moduleImport("re").getMember("escape").getACall() and + regexNode = this.getArg(0) and + regexMethod = this.asExpr().(Attribute) } + + DataFlow::Node getRegexNode() { result = regexNode } + + Attribute getRegexMethod() { result = regexMethod } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 102fb80cfff..f82af085db0 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -20,5 +20,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } - override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape } + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer = sanitizer.(RegexEscape).getRegexNode() + } } From be09ffec3fbb31a69265911cad0d09989dfe5446 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 10:24:42 +0100 Subject: [PATCH 144/550] Create RegexEscape Range --- .../experimental/semmle/python/Concepts.qll | 42 +++++++++++++------ .../semmle/python/frameworks/Stdlib.qll | 15 +++++++ .../security/injection/RegexInjection.qll | 2 +- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b8cef8e4e11..b4723c9e99d 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -18,7 +18,7 @@ private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that works with regular expressions. + * A data-flow node that works with regular expressions immediately executing an expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. @@ -31,7 +31,7 @@ module RegexExecution { } /** - * A data-flow node that works with regular expressions. + * A data-flow node that works with regular expressions immediately executing an expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. @@ -46,17 +46,33 @@ class RegexExecution extends DataFlow::Node { Attribute getRegexMethod() { result = range.getRegexMethod() } } -class RegexEscape extends DataFlow::CallCfgNode { - DataFlow::Node regexNode; - Attribute regexMethod; +/** Provides classes for modeling Regular Expression escape-related APIs. */ +module RegexEscape { + /** + * A data-flow node that collects functions escaping regular expressions. + * + * Extend this class to model new APIs. If you want to refine existing API models, + * extend `RegexEscape` instead. + */ + abstract class Range extends DataFlow::Node { + abstract DataFlow::Node getRegexNode(); - RegexEscape() { - this = API::moduleImport("re").getMember("escape").getACall() and - regexNode = this.getArg(0) and - regexMethod = this.asExpr().(Attribute) + abstract Attribute getEscapeMethod(); } - - DataFlow::Node getRegexNode() { result = regexNode } - - Attribute getRegexMethod() { result = regexMethod } +} + +/** + * A data-flow node that collects functions escaping regular expressions. + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `RegexEscape::Range` instead. + */ +class RegexEscape extends DataFlow::Node { + RegexEscape::Range range; + + RegexEscape() { this = range } + + DataFlow::Node getRegexNode() { result = range.getRegexNode() } + + Attribute getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5a3441bed83..0b5c57c1c67 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -53,4 +53,19 @@ private module Re { override Attribute getRegexMethod() { result = regexMethod } } + + class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { + DataFlow::Node regexNode; + Attribute escapeMethod; + + ReEscape() { + this = API::moduleImport("re").getMember("escape").getACall() and + regexNode = this.getArg(0) and + escapeMethod = this.asExpr().(Attribute) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override Attribute getEscapeMethod() { result = escapeMethod } + } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index f82af085db0..fb2ef2ed192 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -21,6 +21,6 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = sanitizer.(RegexEscape).getRegexNode() + sanitizer = any(RegexEscape reEscape).getRegexNode() } } From c127b109d0ccf29db088f2f2f0e4d58107444df1 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 16:21:34 +0100 Subject: [PATCH 145/550] Create re.compile().ReMethod test --- .../Security/CWE-730/{ => unit_tests}/re_bad.py | 8 ++++++-- .../Security/CWE-730/{ => unit_tests}/re_good.py | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) rename python/ql/src/experimental/Security/CWE-730/{ => unit_tests}/re_bad.py (72%) rename python/ql/src/experimental/Security/CWE-730/{ => unit_tests}/re_good.py (71%) diff --git a/python/ql/src/experimental/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py similarity index 72% rename from python/ql/src/experimental/Security/CWE-730/re_bad.py rename to python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py index 6a0aa7c6614..172fb94bab8 100644 --- a/python/ql/src/experimental/Security/CWE-730/re_bad.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py @@ -1,3 +1,5 @@ +# move outside test folder + from flask import request, Flask import re @@ -7,16 +9,18 @@ app = Flask(__name__) @app.route("/direct") def direct(): pattern = request.args['pattern'] - re.search(pattern, "") @app.route("/compile") def compile(): pattern = re.compile(request.args['pattern']) - pattern.search("") +@app.route("/compile_direct") +def compile_direct(): + re.compile(request.args['pattern']).search("") + # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py similarity index 71% rename from python/ql/src/experimental/Security/CWE-730/re_good.py rename to python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py index ba09b1fe30b..a226d7a8899 100644 --- a/python/ql/src/experimental/Security/CWE-730/re_good.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py @@ -1,3 +1,5 @@ +# move outside test folder + from flask import request, Flask import re @@ -7,16 +9,19 @@ app = Flask(__name__) @app.route("/direct") def direct(): pattern = re.escape(request.args['pattern']) - re.search(pattern, "") @app.route("/compile") def compile(): pattern = re.compile(re.escape(request.args['pattern'])) - pattern.search("") +@app.route("/compile_direct") +def compile_direct(): + re.compile(re.escape(request.args['pattern'])).search("") + + # if __name__ == "__main__": # app.run(debug=True) From 35f1c45d3234ddd679d852f8266d62b9449486bf Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 17:01:53 +0100 Subject: [PATCH 146/550] Change from Attribute to DataFlow::CallCfgNode in getRegexMethod() --- .../src/experimental/semmle/python/Concepts.qll | 8 ++++---- .../semmle/python/frameworks/Stdlib.qll | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b4723c9e99d..8530047e60c 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -26,7 +26,7 @@ module RegexExecution { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - abstract Attribute getRegexMethod(); + abstract DataFlow::CallCfgNode getRegexMethod(); } } @@ -43,7 +43,7 @@ class RegexExecution extends DataFlow::Node { DataFlow::Node getRegexNode() { result = range.getRegexNode() } - Attribute getRegexMethod() { result = range.getRegexMethod() } + DataFlow::CallCfgNode getRegexMethod() { result = range.getRegexMethod() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ @@ -57,7 +57,7 @@ module RegexEscape { abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - abstract Attribute getEscapeMethod(); + abstract DataFlow::CallCfgNode getEscapeMethod(); } } @@ -74,5 +74,5 @@ class RegexEscape extends DataFlow::Node { DataFlow::Node getRegexNode() { result = range.getRegexNode() } - Attribute getEscapeMethod() { result = range.getEscapeMethod() } + DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 0b5c57c1c67..635c7a5803f 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -21,22 +21,22 @@ private module Re { private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - Attribute regexMethod; + DataFlow::CallCfgNode regexMethod; DirectRegex() { this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) and - regexMethod = this.asExpr().(Attribute) + regexMethod = this } override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } + override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - Attribute regexMethod; + DataFlow::CallCfgNode regexMethod; CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | @@ -51,21 +51,21 @@ private module Re { override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getRegexMethod() { result = regexMethod } + override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; - Attribute escapeMethod; + DataFlow::CallCfgNode escapeMethod; ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and regexNode = this.getArg(0) and - escapeMethod = this.asExpr().(Attribute) + escapeMethod = this } override DataFlow::Node getRegexNode() { result = regexNode } - override Attribute getEscapeMethod() { result = escapeMethod } + override DataFlow::CallCfgNode getEscapeMethod() { result = escapeMethod } } } From 36cc7b5e3fee327db28144f142ac94268611a0c6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 17:19:01 +0100 Subject: [PATCH 147/550] Fix CompiledRegex --- .../experimental/semmle/python/frameworks/Stdlib.qll | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 635c7a5803f..44305b0bc03 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -31,6 +31,7 @@ private module Re { override DataFlow::Node getRegexNode() { result = regexNode } + // pending obj.this discussion override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } @@ -39,13 +40,13 @@ private module Re { DataFlow::CallCfgNode regexMethod; CompiledRegex() { - exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod | - this = reMethod and + exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.(DataFlow::AttrRead).getObject().getALocalSource() and + patternCall = reMethod.getObject().getALocalSource() and + reMethod.getAttributeName() instanceof ReMethods and regexNode = patternCall.getArg(0) and - // regexMethod is *not* worked out outside class instanciation because `CompiledRegex` focuses on re.compile(pattern).ReMethod - regexMethod = reMethod.getRegexMethod() + regexMethod = this ) } From 53d61c4fb68445ea88e8b8753aaf65341771dc78 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 26 Mar 2021 19:18:14 +0100 Subject: [PATCH 148/550] Use custom Sink --- .../Security/CWE-730/RegexInjection.ql | 14 ++++++++++---- .../ql/src/experimental/semmle/python/Concepts.qll | 13 +++++++++++++ .../semmle/python/frameworks/Stdlib.qll | 2 ++ .../python/security/injection/RegexInjection.qll | 3 +-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 9d3da12f9bd..ab6d11de53c 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,7 +16,13 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.", - sink.getNode(), "This", source.getNode(), "user-provided value" +from + RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + Attribute sinkAttribute +where + config.hasFlowPath(source, sink) and + sinkAttribute = sink.getNode().(RegexInjectionSink).getRegexMethod() +select sink.getNode(), source, sink, + "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", + source.getNode(), "user-provided value", sinkAttribute, + sinkAttribute.getObject().toString() + "." + sinkAttribute.getName() diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8530047e60c..ab30e97f8d9 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -76,3 +76,16 @@ class RegexEscape extends DataFlow::Node { DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } + +class RegexInjectionSink extends DataFlow::Node { + Attribute regexMethod; + + RegexInjectionSink() { + exists(RegexExecution reExec | + this = reExec.getRegexNode() and + regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + ) + } + + Attribute getRegexMethod() { result = regexMethod } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 44305b0bc03..5ba4a652244 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -24,6 +24,7 @@ private module Re { DataFlow::CallCfgNode regexMethod; DirectRegex() { + // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) and regexMethod = this @@ -41,6 +42,7 @@ private module Re { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | + // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index fb2ef2ed192..c2112175bfa 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -8,7 +8,6 @@ import experimental.semmle.python.Concepts import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources -import semmle.python.ApiGraphs /** * A taint-tracking configuration for detecting regular expression injections. @@ -18,7 +17,7 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink } override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer = any(RegexEscape reEscape).getRegexNode() From 18ce257fc8bea35cdbc97437fd04dd1da9957a1e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 00:26:30 +0100 Subject: [PATCH 149/550] Move RegexInjectionSink to query config (qll) --- .../ql/src/experimental/semmle/python/Concepts.qll | 13 ------------- .../python/security/injection/RegexInjection.qll | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index ab30e97f8d9..8530047e60c 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -76,16 +76,3 @@ class RegexEscape extends DataFlow::Node { DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } - -class RegexInjectionSink extends DataFlow::Node { - Attribute regexMethod; - - RegexInjectionSink() { - exists(RegexExecution reExec | - this = reExec.getRegexNode() and - regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) - ) - } - - Attribute getRegexMethod() { result = regexMethod } -} diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index c2112175bfa..358d884687b 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -9,6 +9,19 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +class RegexInjectionSink extends DataFlow::Node { + Attribute regexMethod; + + RegexInjectionSink() { + exists(RegexExecution reExec | + this = reExec.getRegexNode() and + regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + ) + } + + Attribute getRegexMethod() { result = regexMethod } +} + /** * A taint-tracking configuration for detecting regular expression injections. */ From e78e2ac2667740a7899436ffeae8bf9e1b4e2dfa Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 00:36:00 +0100 Subject: [PATCH 150/550] Get rid of (get)regexMethod --- .../src/experimental/semmle/python/Concepts.qll | 8 -------- .../semmle/python/frameworks/Stdlib.qll | 16 +++------------- .../python/security/injection/RegexInjection.qll | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 8530047e60c..46bb8ad4ee3 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -25,8 +25,6 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - - abstract DataFlow::CallCfgNode getRegexMethod(); } } @@ -42,8 +40,6 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } - - DataFlow::CallCfgNode getRegexMethod() { result = range.getRegexMethod() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ @@ -56,8 +52,6 @@ module RegexEscape { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); - - abstract DataFlow::CallCfgNode getEscapeMethod(); } } @@ -73,6 +67,4 @@ class RegexEscape extends DataFlow::Node { RegexEscape() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } - - DataFlow::CallCfgNode getEscapeMethod() { result = range.getEscapeMethod() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5ba4a652244..28f07eddff4 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -26,14 +26,10 @@ private module Re { DirectRegex() { // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and - regexNode = this.getArg(0) and - regexMethod = this + regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } - - // pending obj.this discussion - override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { @@ -47,14 +43,11 @@ private module Re { patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and reMethod.getAttributeName() instanceof ReMethods and - regexNode = patternCall.getArg(0) and - regexMethod = this + regexNode = patternCall.getArg(0) ) } override DataFlow::Node getRegexNode() { result = regexNode } - - override DataFlow::CallCfgNode getRegexMethod() { result = regexMethod } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { @@ -63,12 +56,9 @@ private module Re { ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and - regexNode = this.getArg(0) and - escapeMethod = this + regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } - - override DataFlow::CallCfgNode getEscapeMethod() { result = escapeMethod } } } diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 358d884687b..b77eae22371 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -15,7 +15,7 @@ class RegexInjectionSink extends DataFlow::Node { RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexMethod = reExec.getRegexMethod().getFunction().asExpr().(Attribute) + regexMethod = reExec.asExpr().(Attribute) ) } From a5850f4a9908e99df007cabaeae2460d920b4772 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 11:32:58 +0100 Subject: [PATCH 151/550] Use getRegexModule to know used lib --- .../src/experimental/Security/CWE-730/RegexInjection.ql | 8 ++++---- python/ql/src/experimental/semmle/python/Concepts.qll | 4 ++++ .../src/experimental/semmle/python/frameworks/Stdlib.qll | 7 ++++--- .../semmle/python/security/injection/RegexInjection.qll | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index ab6d11de53c..422c57c9bff 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -18,11 +18,11 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - Attribute sinkAttribute + RegexInjectionSink castedSink where config.hasFlowPath(source, sink) and - sinkAttribute = sink.getNode().(RegexInjectionSink).getRegexMethod() + castedSink = sink.getNode() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", sinkAttribute, - sinkAttribute.getObject().toString() + "." + sinkAttribute.getName() + source.getNode(), "user-provided value", castedSink, + castedSink.getRegexModule() + "." + castedSink.asExpr().(Attribute).getName() diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 46bb8ad4ee3..6bdf1a37bb0 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -25,6 +25,8 @@ module RegexExecution { */ abstract class Range extends DataFlow::Node { abstract DataFlow::Node getRegexNode(); + + abstract string getRegexModule(); } } @@ -40,6 +42,8 @@ class RegexExecution extends DataFlow::Node { RegexExecution() { this = range } DataFlow::Node getRegexNode() { result = range.getRegexNode() } + + string getRegexModule() { result = range.getRegexModule() } } /** Provides classes for modeling Regular Expression escape-related APIs. */ diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 28f07eddff4..a4a860711b8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -21,15 +21,15 @@ private module Re { private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode regexMethod; DirectRegex() { - // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and regexNode = this.getArg(0) } override DataFlow::Node getRegexNode() { result = regexNode } + + override string getRegexModule() { result = "re" } } private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { @@ -38,7 +38,6 @@ private module Re { CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | - // this.getLocation().getFile().getBaseName().regexpMatch("^re_(good|bad)\\.py$") and // debug this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and @@ -48,6 +47,8 @@ private module Re { } override DataFlow::Node getRegexNode() { result = regexNode } + + override string getRegexModule() { result = "re" } } class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index b77eae22371..c4e06abf390 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -10,16 +10,16 @@ import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources class RegexInjectionSink extends DataFlow::Node { - Attribute regexMethod; + string regexModule; RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexMethod = reExec.asExpr().(Attribute) + regexModule = reExec.getRegexModule() ) } - Attribute getRegexMethod() { result = regexMethod } + string getRegexModule() { result = regexModule } } /** From f75110365fdc9cef4c18ba4a5395f1f8699bd54b Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 19:54:30 +0100 Subject: [PATCH 152/550] Fix Sink utilization in select --- .../experimental/Security/CWE-730/RegexInjection.ql | 13 +++++-------- .../python/security/injection/RegexInjection.qll | 6 +++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 422c57c9bff..73178ca1c03 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,13 +16,10 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from - RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - RegexInjectionSink castedSink -where - config.hasFlowPath(source, sink) and - castedSink = sink.getNode() +from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", castedSink, - castedSink.getRegexModule() + "." + castedSink.asExpr().(Attribute).getName() + source.getNode(), "user-provided value", sink.getNode(), + sink.getNode().(RegexInjectionSink).getRegexModule() + "." + + sink.getNode().(RegexInjectionSink).getRegexMethod().getName() diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index c4e06abf390..2f0fa71ef79 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -11,15 +11,19 @@ import semmle.python.dataflow.new.RemoteFlowSources class RegexInjectionSink extends DataFlow::Node { string regexModule; + Attribute regexMethod; RegexInjectionSink() { exists(RegexExecution reExec | this = reExec.getRegexNode() and - regexModule = reExec.getRegexModule() + regexModule = reExec.getRegexModule() and + regexMethod = reExec.(DataFlow::CallCfgNode).getFunction().asExpr().(Attribute) ) } string getRegexModule() { result = regexModule } + + Attribute getRegexMethod() { result = regexMethod } } /** From 66ee67a7817037495f28909d2c6b75be4006a7e7 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 19:59:39 +0100 Subject: [PATCH 153/550] Polished select statement --- .../Security/CWE-730/RegexInjection.ql | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 73178ca1c03..77355fdf30d 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -16,10 +16,14 @@ import python import experimental.semmle.python.security.injection.RegexInjection import DataFlow::PathGraph -from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) +from + RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, + RegexInjectionSink castedSink, Attribute methodAttribute +where + config.hasFlowPath(source, sink) and + castedSink = sink.getNode() and + methodAttribute = castedSink.getRegexMethod() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", - source.getNode(), "user-provided value", sink.getNode(), - sink.getNode().(RegexInjectionSink).getRegexModule() + "." + - sink.getNode().(RegexInjectionSink).getRegexMethod().getName() + source.getNode(), "user-provided value", methodAttribute, + castedSink.getRegexModule() + "." + methodAttribute.getName() From c54f08f33a00571ac2b1454c87b2cefbfd3e8b4d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:13:32 +0100 Subject: [PATCH 154/550] Improve qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 65 ++++++++++++++----- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 2e3d238daa4..0fc0445e07b 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,23 +1,52 @@ - + - - -

    If a regular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.

    -
    + +

    + Constructing a regular expression with unsanitized user input is dangerous as a malicious user may + be able to modify the meaning of the expression. In particular, such a user may be able to provide + a regular expression fragment that takes exponential time in the worst case, and use that to + perform a Denial of Service attack. +

    +
    - -

    In case user input must compose a regular expression, it should be escaped with functions such as re.escape. - + +

    + Before embedding user input into a regular expression, use a sanitization function such as + re.escape to escape meta-characters that have a special meaning regarding + regular expressions' syntax. +

    +
    - -
  • - OWASP - Regular Expression DoS -
  • -
  • - SonarSource - RSPEC-2631 -
  • -
    + +

    + The following examples are based on a simple Flask web server environment. +

    +

    + The following example shows a HTTP request parameter that is used to construct a regular expression + without sanitizing it first: +

    + +

    + Instead, the request parameter should be sanitized first, for example using the function + re.escape. This ensures that the user cannot insert characters which have a + special meaning in regular expressions. +

    + +
    + +
  • + OWASP: + Regular expression Denial of Service - ReDoS. +
  • +
  • + Wikipedia: ReDoS. +
  • +
  • + Python docs: re. +
  • +
  • + SonarSource: RSPEC-2631 +
  • +
    \ No newline at end of file From 0e169ba10ebfbc1c0fe56fc347d3abb857577b9f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:14:11 +0100 Subject: [PATCH 155/550] Format qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 0fc0445e07b..3e635017da1 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,52 +1,52 @@ - + + + +

    + Constructing a regular expression with unsanitized user input is dangerous as a malicious user may + be able to modify the meaning of the expression. In particular, such a user may be able to provide + a regular expression fragment that takes exponential time in the worst case, and use that to + perform a Denial of Service attack. +

    +
    - -

    - Constructing a regular expression with unsanitized user input is dangerous as a malicious user may - be able to modify the meaning of the expression. In particular, such a user may be able to provide - a regular expression fragment that takes exponential time in the worst case, and use that to - perform a Denial of Service attack. -

    -
    + +

    + Before embedding user input into a regular expression, use a sanitization function such as + re.escape to escape meta-characters that have a special meaning regarding + regular expressions' syntax. +

    +
    - -

    - Before embedding user input into a regular expression, use a sanitization function such as - re.escape to escape meta-characters that have a special meaning regarding - regular expressions' syntax. -

    -
    + +

    + The following examples are based on a simple Flask web server environment. +

    +

    + The following example shows a HTTP request parameter that is used to construct a regular expression + without sanitizing it first: +

    + +

    + Instead, the request parameter should be sanitized first, for example using the function + re.escape. This ensures that the user cannot insert characters which have a + special meaning in regular expressions. +

    + +
    - -

    - The following examples are based on a simple Flask web server environment. -

    -

    - The following example shows a HTTP request parameter that is used to construct a regular expression - without sanitizing it first: -

    - -

    - Instead, the request parameter should be sanitized first, for example using the function - re.escape. This ensures that the user cannot insert characters which have a - special meaning in regular expressions. -

    - -
    - - -
  • - OWASP: - Regular expression Denial of Service - ReDoS. -
  • -
  • - Wikipedia: ReDoS. -
  • -
  • - Python docs: re. -
  • -
  • - SonarSource: RSPEC-2631 -
  • -
    + +
  • + OWASP: + Regular expression Denial of Service - ReDoS. +
  • +
  • + Wikipedia: ReDoS. +
  • +
  • + Python docs: re. +
  • +
  • + SonarSource: RSPEC-2631 +
  • +
    \ No newline at end of file From d49c23fe67035e676148ee9b690b00e2e894ada0 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:26:55 +0100 Subject: [PATCH 156/550] Improve tests' readability --- .../Security/CWE-730/unit_tests/re_bad.py | 30 +++++++++++++---- .../Security/CWE-730/unit_tests/re_good.py | 32 +++++++++++++++---- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py index 172fb94bab8..55b306c8857 100644 --- a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py @@ -1,5 +1,3 @@ -# move outside test folder - from flask import request, Flask import re @@ -8,19 +6,37 @@ app = Flask(__name__) @app.route("/direct") def direct(): - pattern = request.args['pattern'] - re.search(pattern, "") + """ + A RemoteFlowSource is used directly as re.search's pattern + """ + unsafe_pattern = request.args["pattern"] + re.search(unsafe_pattern, "") + + +# A RemoteFlowSource is used directly as re.compile's pattern @app.route("/compile") def compile(): - pattern = re.compile(request.args['pattern']) - pattern.search("") + """ + A RemoteFlowSource is used directly as re.compile's pattern + which also executes .search() + """ + + unsafe_pattern = request.args["pattern"] + compiled_pattern = re.compile(unsafe_pattern) + compiled_pattern.search("") @app.route("/compile_direct") def compile_direct(): - re.compile(request.args['pattern']).search("") + """ + A RemoteFlowSource is used directly as re.compile's pattern + which also executes .search() in the same line + """ + + unsafe_pattern = request.args["pattern"] + re.compile(unsafe_pattern).search("") # if __name__ == "__main__": # app.run(debug=True) diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py index a226d7a8899..6dc58b87f85 100644 --- a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py +++ b/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py @@ -1,5 +1,3 @@ -# move outside test folder - from flask import request, Flask import re @@ -8,19 +6,39 @@ app = Flask(__name__) @app.route("/direct") def direct(): - pattern = re.escape(request.args['pattern']) - re.search(pattern, "") + """ + A RemoteFlowSource is escaped by re.escape and then used as + re'search pattern + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.search(safe_pattern, "") @app.route("/compile") def compile(): - pattern = re.compile(re.escape(request.args['pattern'])) - pattern.search("") + """ + A RemoteFlowSource is escaped by re.escape and used as re.compile's + pattern which also executes .search() + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + compiled_pattern = re.compile(safe_pattern) + compiled_pattern.search("") @app.route("/compile_direct") def compile_direct(): - re.compile(re.escape(request.args['pattern'])).search("") + """ + A RemoteFlowSource is escaped by re.escape and then used as re.compile's + pattern which also executes .search() in the same line + """ + + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.compile(safe_pattern).search("") # if __name__ == "__main__": From d4a89b2fd822689f626523d0da786f077ca7464f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:29:11 +0100 Subject: [PATCH 157/550] Fix qhelp typo while converting to python's regex injection --- .../ql/src/experimental/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 3e635017da1..3c1d5907a77 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -31,7 +31,7 @@ re.escape. This ensures that the user cannot insert characters which have a special meaning in regular expressions.

    - + From b6721971dd9149992fd53524a99f878c7220fcc2 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 20:54:46 +0100 Subject: [PATCH 158/550] Improve code comments --- .../experimental/semmle/python/Concepts.qll | 9 +++++ .../semmle/python/frameworks/Stdlib.qll | 38 ++++++++++++++++--- .../security/injection/RegexInjection.qll | 5 +++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 6bdf1a37bb0..b325925f321 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -24,8 +24,14 @@ module RegexExecution { * extend `RegexExecution` instead. */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the executed expression. + */ abstract DataFlow::Node getRegexNode(); + /** + * Gets the library used to execute the regular expression. + */ abstract string getRegexModule(); } } @@ -55,6 +61,9 @@ module RegexEscape { * extend `RegexEscape` instead. */ abstract class Range extends DataFlow::Node { + /** + * Gets the argument containing the escaped expression. + */ abstract DataFlow::Node getRegexNode(); } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index a4a860711b8..c825bcfc086 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -10,20 +10,33 @@ private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs -/** Provides models for the Python standard library. */ +/** + * Provides models for Python's `re` library. + * + * See https://docs.python.org/3/library/re.html + */ private module Re { - /** List of re methods. */ - private class ReMethods extends string { - ReMethods() { + /** + * List of `re` methods immediately executing an expression. + * + * See https://docs.python.org/3/library/re.html#module-contents + */ + private class RegexExecutionMethods extends string { + RegexExecutionMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] } } + /** + * A class to find `re` methods immediately executing an expression. + * + * See `RegexExecutionMethods` + */ private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; DirectRegex() { - this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and + this = API::moduleImport("re").getMember(any(RegexExecutionMethods m)).getACall() and regexNode = this.getArg(0) } @@ -32,6 +45,14 @@ private module Re { override string getRegexModule() { result = "re" } } + /** + * A class to find `re` methods immediately executing an expression from a + * compiled expression by `re.compile`. + * + * See `RegexExecutionMethods` + * + * See https://docs.python.org/3/library/re.html#regular-expression-objects + */ private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; DataFlow::CallCfgNode regexMethod; @@ -41,7 +62,7 @@ private module Re { this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and patternCall = reMethod.getObject().getALocalSource() and - reMethod.getAttributeName() instanceof ReMethods and + reMethod.getAttributeName() instanceof RegexExecutionMethods and regexNode = patternCall.getArg(0) ) } @@ -51,6 +72,11 @@ private module Re { override string getRegexModule() { result = "re" } } + /** + * A class to find `re` methods escaping an expression. + * + * See https://docs.python.org/3/library/re.html#re.escape + */ class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; DataFlow::CallCfgNode escapeMethod; diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 2f0fa71ef79..2ce6f06edc0 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -9,6 +9,11 @@ import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.RemoteFlowSources +/** + * A class to find methods executing regular expressions. + * + * See `RegexExecution` + */ class RegexInjectionSink extends DataFlow::Node { string regexModule; Attribute regexMethod; From 36555149245fc55d0fd72dc1953342956a386c71 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:01:07 +0100 Subject: [PATCH 159/550] Fix ambiguity --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index c825bcfc086..849dd47467d 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -46,8 +46,7 @@ private module Re { } /** - * A class to find `re` methods immediately executing an expression from a - * compiled expression by `re.compile`. + * A class to find `re` methods immediately executing a compiled expression by `re.compile`. * * See `RegexExecutionMethods` * From fc27c6c5470043c4c6b5cf96d2abded89ef152e6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:03:19 +0100 Subject: [PATCH 160/550] Fix RegexExecution ambiguity --- python/ql/src/experimental/semmle/python/Concepts.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index b325925f321..08131064912 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -18,7 +18,7 @@ private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that works with regular expressions immediately executing an expression. + * A data-flow node that collects methods immediately executing an expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. @@ -37,7 +37,7 @@ module RegexExecution { } /** - * A data-flow node that works with regular expressions immediately executing an expression. + * A data-flow node that collect methods immediately executing an expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. From 03825a6052a1d9412e02aaca7990f094e596f9a3 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:05:49 +0100 Subject: [PATCH 161/550] Add comment to Sink's predicates --- .../semmle/python/security/injection/RegexInjection.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index 2ce6f06edc0..a7a0526f750 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -26,8 +26,14 @@ class RegexInjectionSink extends DataFlow::Node { ) } + /** + * Gets the argument containing the executed expression. + */ string getRegexModule() { result = regexModule } + /** + * Gets the mthod used to execute the regular expression. + */ Attribute getRegexMethod() { result = regexMethod } } From ec85ee453777eed7053859692a257cb89da5400f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 27 Mar 2021 21:07:53 +0100 Subject: [PATCH 162/550] Sink's predicate typo --- .../semmle/python/security/injection/RegexInjection.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll index a7a0526f750..7b7b08cacab 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll @@ -32,7 +32,7 @@ class RegexInjectionSink extends DataFlow::Node { string getRegexModule() { result = regexModule } /** - * Gets the mthod used to execute the regular expression. + * Gets the method used to execute the regular expression. */ Attribute getRegexMethod() { result = regexMethod } } From d401d18e7193558eedd1c17c82b6f46ef504d39f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 01:22:12 +0100 Subject: [PATCH 163/550] Add .expected and qlref --- .../Security/CWE-730/RegexInjection.expected | 27 +++++++++++++++++++ .../Security/CWE-730/RegexInjection.qlref | 1 + 2 files changed, 28 insertions(+) create mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.expected create mode 100644 python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected b/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected new file mode 100644 index 00000000000..9886df2904d --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected @@ -0,0 +1,27 @@ +edges +| re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | +| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | +| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | +| re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | +| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | +| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | +| re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | +| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | +| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | +nodes +| re_bad.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:26:22:26:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:38:22:38:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +#select +| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search | +| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:26:22:26:28 | ControlFlowNode for request | user-provided value | re_bad.py:28:5:28:27 | Attribute | re.search | +| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:38:22:38:28 | ControlFlowNode for request | user-provided value | re_bad.py:39:5:39:37 | Attribute | re.search | diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref new file mode 100644 index 00000000000..c0c506c4707 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE-730/RegexInjection.ql From 81d23c066c4fbad99914aa3410e57e328c5c2375 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 01:37:55 +0100 Subject: [PATCH 164/550] Move tests and qlref from /src to /test --- .../query-tests}/Security/CWE-730/RegexInjection.qlref | 0 .../experimental/query-tests/Security/CWE-730}/re_bad.py | 0 .../experimental/query-tests/Security/CWE-730}/re_good.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename python/ql/{src/experimental => test/experimental/query-tests}/Security/CWE-730/RegexInjection.qlref (100%) rename python/ql/{src/experimental/Security/CWE-730/unit_tests => test/experimental/query-tests/Security/CWE-730}/re_bad.py (100%) rename python/ql/{src/experimental/Security/CWE-730/unit_tests => test/experimental/query-tests/Security/CWE-730}/re_good.py (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.qlref rename to python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.qlref diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/unit_tests/re_bad.py rename to python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py diff --git a/python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/unit_tests/re_good.py rename to python/ql/test/experimental/query-tests/Security/CWE-730/re_good.py From d968eea9143fda8d6118d1cf906f0a23fd673065 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sun, 28 Mar 2021 18:36:59 +0200 Subject: [PATCH 165/550] Move expected to /test --- .../query-tests}/Security/CWE-730/RegexInjection.expected | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/ql/{src/experimental => test/experimental/query-tests}/Security/CWE-730/RegexInjection.expected (100%) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected similarity index 100% rename from python/ql/src/experimental/Security/CWE-730/RegexInjection.expected rename to python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected From 6a20a4dcc32f9dc0016e39446c579de4e5389b0e Mon Sep 17 00:00:00 2001 From: jorgectf Date: Mon, 29 Mar 2021 15:01:58 +0200 Subject: [PATCH 166/550] Add newline to qhelp --- .../ql/src/experimental/Security/CWE-730/RegexInjection.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 3c1d5907a77..94e66b2b89c 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -49,4 +49,4 @@ SonarSource: RSPEC-2631 -
    \ No newline at end of file +
    From 3fae3fd93e25a7913d762a00d0869a4c3cfabd13 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 30 Mar 2021 17:39:27 +0200 Subject: [PATCH 167/550] Take ApiGraphs out of Concepts.qll --- python/ql/src/experimental/semmle/python/Concepts.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 08131064912..07bbae2a060 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -13,7 +13,6 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.TaintTracking private import experimental.semmle.python.Frameworks -private import semmle.python.ApiGraphs /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { From 05ee853c4e380f5580b17620fce1e4aa4ca14ab6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 6 Apr 2021 15:52:31 +0200 Subject: [PATCH 168/550] Remove wrong comment --- .../ql/test/experimental/query-tests/Security/CWE-730/re_bad.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py index 55b306c8857..622eaf199f6 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/re_bad.py @@ -14,8 +14,6 @@ def direct(): re.search(unsafe_pattern, "") -# A RemoteFlowSource is used directly as re.compile's pattern - @app.route("/compile") def compile(): """ From 12ccd7e3b643899b1abff49056390e6adb40a763 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:20:09 +0200 Subject: [PATCH 169/550] Update .expected --- .../Security/CWE-730/RegexInjection.expected | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected index 9886df2904d..97ed0d1bdfa 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected +++ b/python/ql/test/experimental/query-tests/Security/CWE-730/RegexInjection.expected @@ -2,26 +2,26 @@ edges | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | -| re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | -| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | -| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | -| re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | -| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | -| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | +| re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | +| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | +| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | +| re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | +| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | +| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | nodes | re_bad.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | -| re_bad.py:26:22:26:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| re_bad.py:26:22:26:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| re_bad.py:26:22:26:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | -| re_bad.py:38:22:38:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| re_bad.py:38:22:38:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| re_bad.py:38:22:38:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:24:22:24:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | +| re_bad.py:36:22:36:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern | #select | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search | -| re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | re_bad.py:26:22:26:28 | ControlFlowNode for request | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:27:35:27:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:26:22:26:28 | ControlFlowNode for request | user-provided value | re_bad.py:28:5:28:27 | Attribute | re.search | -| re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | re_bad.py:38:22:38:28 | ControlFlowNode for request | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:39:16:39:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:38:22:38:28 | ControlFlowNode for request | user-provided value | re_bad.py:39:5:39:37 | Attribute | re.search | +| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:27 | Attribute | re.search | +| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:36:22:36:28 | ControlFlowNode for request | user-provided value | re_bad.py:37:5:37:37 | Attribute | re.search | From c4322848ec8525dc70ed5c7ee7d6381362621aeb Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 8 Apr 2021 22:42:35 +0200 Subject: [PATCH 170/550] Polish qhelp --- .../Security/CWE-730/RegexInjection.qhelp | 87 +++++++++---------- .../experimental/Security/CWE-730/re_bad.py | 15 ++++ .../experimental/Security/CWE-730/re_good.py | 17 ++++ 3 files changed, 72 insertions(+), 47 deletions(-) create mode 100644 python/ql/src/experimental/Security/CWE-730/re_bad.py create mode 100644 python/ql/src/experimental/Security/CWE-730/re_good.py diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp index 94e66b2b89c..f19f0744469 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp @@ -1,52 +1,45 @@ - + - -

    - Constructing a regular expression with unsanitized user input is dangerous as a malicious user may - be able to modify the meaning of the expression. In particular, such a user may be able to provide - a regular expression fragment that takes exponential time in the worst case, and use that to - perform a Denial of Service attack. -

    -
    + +

    +Constructing a regular expression with unsanitized user input is dangerous as a malicious user may +be able to modify the meaning of the expression. In particular, such a user may be able to provide +a regular expression fragment that takes exponential time in the worst case, and use that to +perform a Denial of Service attack. +

    +
    - -

    - Before embedding user input into a regular expression, use a sanitization function such as - re.escape to escape meta-characters that have a special meaning regarding - regular expressions' syntax. -

    -
    + +

    +Before embedding user input into a regular expression, use a sanitization function such as +re.escape to escape meta-characters that have a special meaning regarding +regular expressions' syntax. +

    +
    - -

    - The following examples are based on a simple Flask web server environment. -

    -

    - The following example shows a HTTP request parameter that is used to construct a regular expression - without sanitizing it first: -

    - -

    - Instead, the request parameter should be sanitized first, for example using the function - re.escape. This ensures that the user cannot insert characters which have a - special meaning in regular expressions. -

    - -
    + +

    +The following examples are based on a simple Flask web server environment. +

    +

    +The following example shows a HTTP request parameter that is used to construct a regular expression +without sanitizing it first: +

    + +

    +Instead, the request parameter should be sanitized first, for example using the function +re.escape. This ensures that the user cannot insert characters which have a +special meaning in regular expressions. +

    + +
    - -
  • - OWASP: - Regular expression Denial of Service - ReDoS. -
  • -
  • - Wikipedia: ReDoS. -
  • -
  • - Python docs: re. -
  • -
  • - SonarSource: RSPEC-2631 -
  • -
    + +
  • OWASP: Regular expression Denial of Service - ReDoS.
  • +
  • Wikipedia: ReDoS.
  • +
  • Python docs: re.
  • +
  • SonarSource: RSPEC-2631.
  • +
    diff --git a/python/ql/src/experimental/Security/CWE-730/re_bad.py b/python/ql/src/experimental/Security/CWE-730/re_bad.py new file mode 100644 index 00000000000..3befaba9a01 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/re_bad.py @@ -0,0 +1,15 @@ +from flask import request, Flask +import re + + +@app.route("/direct") +def direct(): + unsafe_pattern = request.args["pattern"] + re.search(unsafe_pattern, "") + + +@app.route("/compile") +def compile(): + unsafe_pattern = request.args["pattern"] + compiled_pattern = re.compile(unsafe_pattern) + compiled_pattern.search("") diff --git a/python/ql/src/experimental/Security/CWE-730/re_good.py b/python/ql/src/experimental/Security/CWE-730/re_good.py new file mode 100644 index 00000000000..cdc9a7ac158 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-730/re_good.py @@ -0,0 +1,17 @@ +from flask import request, Flask +import re + + +@app.route("/direct") +def direct(): + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + re.search(safe_pattern, "") + + +@app.route("/compile") +def compile(): + unsafe_pattern = request.args['pattern'] + safe_pattern = re.escape(unsafe_pattern) + compiled_pattern = re.compile(safe_pattern) + compiled_pattern.search("") From c0c71c509c4f9bbf31b665763ed89deeebae90c3 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Tue, 27 Apr 2021 19:21:43 +0200 Subject: [PATCH 171/550] Apply suggestions from code review Update `RegexExecution` docs and use `flowsTo()` instead of `getALocalSource()`. Co-authored-by: yoff --- python/ql/src/experimental/semmle/python/Concepts.qll | 2 +- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 07bbae2a060..e11e21da914 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -17,7 +17,7 @@ private import experimental.semmle.python.Frameworks /** Provides classes for modeling Regular Expression-related APIs. */ module RegexExecution { /** - * A data-flow node that collects methods immediately executing an expression. + * A data-flow node that executes a regular expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexExecution` instead. diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 849dd47467d..1e3478c55f5 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -60,7 +60,7 @@ private module Re { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | this.getFunction() = reMethod and patternCall = API::moduleImport("re").getMember("compile").getACall() and - patternCall = reMethod.getObject().getALocalSource() and + patternCall.flowsTo(reMethod.getObject()) and reMethod.getAttributeName() instanceof RegexExecutionMethods and regexNode = patternCall.getArg(0) ) From 20b532ec5e3b57790cf20ea7ebc587b9fcadc10d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:24:25 +0200 Subject: [PATCH 172/550] Update to-cast sink's naming Signed-off-by: jorgectf --- .../src/experimental/Security/CWE-730/RegexInjection.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql index 77355fdf30d..7725f636eb0 100644 --- a/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql +++ b/python/ql/src/experimental/Security/CWE-730/RegexInjection.ql @@ -18,12 +18,12 @@ import DataFlow::PathGraph from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, - RegexInjectionSink castedSink, Attribute methodAttribute + RegexInjectionSink regexInjectionSink, Attribute methodAttribute where config.hasFlowPath(source, sink) and - castedSink = sink.getNode() and - methodAttribute = castedSink.getRegexMethod() + regexInjectionSink = sink.getNode() and + methodAttribute = regexInjectionSink.getRegexMethod() select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@ and executed by $@.", sink.getNode(), "This", source.getNode(), "user-provided value", methodAttribute, - castedSink.getRegexModule() + "." + methodAttribute.getName() + regexInjectionSink.getRegexModule() + "." + methodAttribute.getName() From 8a800986a290a89492df3d6161d4547d80015bf4 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:26:04 +0200 Subject: [PATCH 173/550] Remove unused class variables Signed-off-by: jorgectf --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 1e3478c55f5..c7a8842cad7 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -54,7 +54,6 @@ private module Re { */ private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode regexMethod; CompiledRegex() { exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod | @@ -78,7 +77,6 @@ private module Re { */ class ReEscape extends DataFlow::CallCfgNode, RegexEscape::Range { DataFlow::Node regexNode; - DataFlow::CallCfgNode escapeMethod; ReEscape() { this = API::moduleImport("re").getMember("escape").getACall() and From 21e01b809fe169f281dbc539e1cc6460dd22edf3 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Tue, 27 Apr 2021 19:52:26 +0200 Subject: [PATCH 174/550] Add code example in CompiledRegex Signed-off-by: jorgectf --- .../experimental/semmle/python/frameworks/Stdlib.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index c7a8842cad7..9bda2cb3158 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -48,6 +48,17 @@ private module Re { /** * A class to find `re` methods immediately executing a compiled expression by `re.compile`. * + * Given the following example: + * + * ```py + * pattern = re.compile(input) + * input.match(s) + * ``` + * + * `patternCall` refers to `re.compile(input)`, + * `regexNode` refers to `input` and + * `this` will refer to `input.match(s)` + * * See `RegexExecutionMethods` * * See https://docs.python.org/3/library/re.html#regular-expression-objects From 8b9c5f8228bf5c2cf8c6cddcc35744fda16a26d0 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 28 Apr 2021 11:50:06 +0200 Subject: [PATCH 175/550] Python/JS: Remove "Only added to aid with internal rewrite" --- .../javascript/security/internal/SensitiveDataHeuristics.qll | 2 -- .../semmle/python/security/internal/SensitiveDataHeuristics.qll | 2 -- 2 files changed, 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index 71f64d98e70..592b6c23f29 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -102,13 +102,11 @@ module HeuristicNames { /** * DEPRECATED: Use `maybeSensitiveRegexp` instead. - * Only added to aid with internal rewrite */ deprecated predicate maybeSensitive = maybeSensitiveRegexp/1; /** * DEPRECATED: Use `notSensitiveRegexp` instead. - * Only added to aid with internal rewrite */ deprecated predicate notSensitive = notSensitiveRegexp/0; diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index 71f64d98e70..592b6c23f29 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -102,13 +102,11 @@ module HeuristicNames { /** * DEPRECATED: Use `maybeSensitiveRegexp` instead. - * Only added to aid with internal rewrite */ deprecated predicate maybeSensitive = maybeSensitiveRegexp/1; /** * DEPRECATED: Use `notSensitiveRegexp` instead. - * Only added to aid with internal rewrite */ deprecated predicate notSensitive = notSensitiveRegexp/0; From 2f14a6218a07b95f6aca2ecb3381ba1997a38e7f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 28 Apr 2021 12:26:02 +0200 Subject: [PATCH 176/550] generalize RxJS pipes --- .../src/semmle/javascript/frameworks/RxJS.qll | 62 +++++++++++++------ .../TaintTracking/BasicTaintTracking.expected | 3 + .../test/library-tests/TaintTracking/rxjs.js | 14 ++++- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll b/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll index aff312ee9b0..abc322de2a7 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll @@ -23,7 +23,7 @@ private class RxJsSubscribeStep extends TaintTracking::SharedTaintStep { * created by the `map` call. */ private DataFlow::Node pipeInput(DataFlow::CallNode pipe) { - pipe = DataFlow::moduleMember("rxjs/operators", ["map", "filter"]).getACall() and + pipe = DataFlow::moduleMember("rxjs/operators", any(string s | not s = "catchError")).getACall() and result = pipe.getCallback(0).getParameter(0) } @@ -48,7 +48,46 @@ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) { * be special-cased. */ private predicate isIdentityPipe(DataFlow::CallNode pipe) { - pipe = DataFlow::moduleMember("rxjs/operators", "catchError").getACall() + pipe = DataFlow::moduleMember("rxjs/operators", ["catchError", "tap"]).getACall() +} + +/** + * A call to `pipe`, which is assumed to be an `rxjs/operators` pipe. + * + * Has utility methods `getInput`/`getOutput` to get the input/output of each + * element of the pipe. + * These utility methods automatically handle itentity pipes, and the + * first/last elements of the pipe. + */ +private class RxJSPipe extends DataFlow::MethodCallNode { + RxJSPipe() { this.getMethodName() = "pipe" } + + /** + * Gets an input to pipe element `i`. + * Or if `i` is equal to the number of elements, gets the output of the pipe (the call itself) + */ + DataFlow::Node getInput(int i) { + result = pipeInput(this.getArgument(i).getALocalSource()) + or + i = this.getNumArgument() and + result = this + } + + /** + * Gets an output from pipe element `i`. + * Handles identity pipes by getting the output from the previous element. + * If `i` is -1, gets the receiver to the call, which started the pipe. + */ + DataFlow::Node getOutput(int i) { + isIdentityPipe(this.getArgument(i).getALocalSource()) and + result = getOutput(i - 1) + or + not isIdentityPipe(this.getArgument(i).getALocalSource()) and + result = pipeOutput(this.getArgument(i).getALocalSource()) + or + i = -1 and + result = this.getReceiver() + } } /** @@ -56,22 +95,9 @@ private predicate isIdentityPipe(DataFlow::CallNode pipe) { */ private class RxJsPipeMapStep extends TaintTracking::SharedTaintStep { override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::MethodCallNode call | call.getMethodName() = "pipe" | - pred = call.getReceiver() and - succ = pipeInput(call.getArgument(0).getALocalSource()) - or - exists(int i | - pred = pipeOutput(call.getArgument(i).getALocalSource()) and - succ = pipeInput(call.getArgument(i + 1).getALocalSource()) - ) - or - pred = pipeOutput(call.getLastArgument().getALocalSource()) and - succ = call - or - // Handle a common case where the last step is `catchError`. - isIdentityPipe(call.getLastArgument().getALocalSource()) and - pred = pipeOutput(call.getArgument(call.getNumArgument() - 2)) and - succ = call + exists(RxJSPipe pipe, int i | + pred = pipe.getOutput(i) and + succ = pipe.getInput(i + 1) ) } } diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 8eae0bb62aa..6f3c00270e6 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -109,6 +109,9 @@ typeInferenceMismatch | promise.js:10:24:10:31 | source() | promise.js:10:8:10:32 | Promise ... urce()) | | promise.js:12:20:12:27 | source() | promise.js:13:8:13:23 | resolver.promise | | rxjs.js:3:1:3:8 | source() | rxjs.js:10:14:10:17 | data | +| rxjs.js:13:1:13:8 | source() | rxjs.js:17:23:17:23 | x | +| rxjs.js:13:1:13:8 | source() | rxjs.js:18:23:18:23 | x | +| rxjs.js:13:1:13:8 | source() | rxjs.js:22:14:22:17 | data | | sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:14:10:14:14 | taint | | sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:33:14:33:18 | taint | | sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/rxjs.js b/javascript/ql/test/library-tests/TaintTracking/rxjs.js index f3ef209d392..70715545e66 100644 --- a/javascript/ql/test/library-tests/TaintTracking/rxjs.js +++ b/javascript/ql/test/library-tests/TaintTracking/rxjs.js @@ -1,4 +1,4 @@ -import { map, catchError } from 'rxjs/operators'; +import { map, tap, catchError } from 'rxjs/operators'; source() .pipe( @@ -9,3 +9,15 @@ source() .subscribe(data => { sink(data) }); + +source() + .pipe( + map(x => x + 'foo'), + // `tap` taps into the source observable, so like `subscribe` but inside the pipe. + tap(x => sink(x)), + tap(x => sink(x)), + catchError(err => {}) + ) + .subscribe(data => { + sink(data) + }); From 902a4368a1f90b84e2b537690fae4f1028f86d18 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 28 Apr 2021 12:36:07 +0200 Subject: [PATCH 177/550] assume that all pipe elements that return something, return outputs --- .../ql/src/semmle/javascript/frameworks/RxJS.qll | 3 ++- .../TaintTracking/BasicTaintTracking.expected | 2 ++ .../ql/test/library-tests/TaintTracking/rxjs.js | 14 +++++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll b/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll index abc322de2a7..8cd05f5047d 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/RxJS.qll @@ -34,7 +34,8 @@ private DataFlow::Node pipeInput(DataFlow::CallNode pipe) { * the pipe. */ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) { - pipe = DataFlow::moduleMember("rxjs/operators", "map").getACall() and + // we assume if there is a return, it is an output. + pipe = DataFlow::moduleMember("rxjs/operators", _).getACall() and result = pipe.getCallback(0).getReturnNode() or pipe = DataFlow::moduleMember("rxjs/operators", "filter").getACall() and diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 6f3c00270e6..0eaafa38965 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -112,6 +112,8 @@ typeInferenceMismatch | rxjs.js:13:1:13:8 | source() | rxjs.js:17:23:17:23 | x | | rxjs.js:13:1:13:8 | source() | rxjs.js:18:23:18:23 | x | | rxjs.js:13:1:13:8 | source() | rxjs.js:22:14:22:17 | data | +| rxjs.js:27:24:27:32 | source(x) | rxjs.js:29:23:29:23 | x | +| rxjs.js:27:24:27:32 | source(x) | rxjs.js:34:14:34:17 | data | | sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:14:10:14:14 | taint | | sanitizer-function.js:12:17:12:24 | source() | sanitizer-function.js:33:14:33:18 | taint | | sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/rxjs.js b/javascript/ql/test/library-tests/TaintTracking/rxjs.js index 70715545e66..2591c6c2364 100644 --- a/javascript/ql/test/library-tests/TaintTracking/rxjs.js +++ b/javascript/ql/test/library-tests/TaintTracking/rxjs.js @@ -1,4 +1,4 @@ -import { map, tap, catchError } from 'rxjs/operators'; +import { map, tap, catchError, switchMap, filter } from 'rxjs/operators'; source() .pipe( @@ -21,3 +21,15 @@ source() .subscribe(data => { sink(data) }); + +myIdentifier() + .pipe( + switchMap(x => source(x)), + filter(x => myFilter(x)), + tap(x => sink(x)), + catchError(err => {}), + map(x => x + 'foo') + ) + .subscribe(data => { + sink(data) + }); From e8347c2c20e6b8c4c150e443372159bcb3034abf Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Apr 2021 10:43:45 +0200 Subject: [PATCH 178/550] C++: Update data-flow caching --- .../cpp/dataflow/internal/DataFlowUtil.qll | 1 - .../dataflow/internal/TaintTrackingUtil.qll | 1 + .../cpp/ir/dataflow/DefaultTaintTracking.qll | 233 ++++++++++-------- .../ir/dataflow/internal/DataFlowDispatch.qll | 4 +- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 21 +- 5 files changed, 143 insertions(+), 117 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 690f24fc59a..75e8c8922d0 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -526,7 +526,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Expr -> Expr exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) 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 591f461c8eb..6216045db32 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -45,6 +45,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent * different objects. */ +cached predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { // Taint can flow through expressions that alter the value but preserve // more than one bit of it _or_ expressions that follow data through diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index 3092031cbc7..49d11a7e3cc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -165,105 +165,132 @@ private predicate nodeIsBarrierEqualityCandidate( any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true) } -private predicate nodeIsBarrier(DataFlow::Node node) { - exists(Variable checkedVar | - readsVariable(node.asInstruction(), checkedVar) and - hasUpperBoundsCheck(checkedVar) - ) - or - exists(Variable checkedVar, Operand access | - /* - * This node is guarded by a condition that forces the accessed variable - * to equal something else. For example: - * ``` - * x = taintsource() - * if (x == 10) { - * taintsink(x); // not considered tainted - * } - * ``` - */ - - nodeIsBarrierEqualityCandidate(node, access, checkedVar) and - readsVariable(access.getDef(), checkedVar) - ) -} - -private predicate nodeIsBarrierIn(DataFlow::Node node) { - // don't use dataflow into taint sources, as this leads to duplicate results. - exists(Expr source | isUserInput(source, _) | - node = DataFlow::exprNode(source) +cached +private module Cached { + cached + predicate nodeIsBarrier(DataFlow::Node node) { + exists(Variable checkedVar | + readsVariable(node.asInstruction(), checkedVar) and + hasUpperBoundsCheck(checkedVar) + ) or - // This case goes together with the similar (but not identical) rule in - // `getNodeForSource`. - node = DataFlow::definitionByReferenceNodeFromArgument(source) - ) - or - // don't use dataflow into binary instructions if both operands are unpredictable - exists(BinaryInstruction iTo | - iTo = node.asInstruction() and - not predictableInstruction(iTo.getLeft()) and - not predictableInstruction(iTo.getRight()) and - // propagate taint from either the pointer or the offset, regardless of predictability - not iTo instanceof PointerArithmeticInstruction - ) - or - // don't use dataflow through calls to pure functions if two or more operands - // are unpredictable - exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | - iTo = node.asInstruction() and - isPureFunction(iTo.getStaticCallTarget().getName()) and - iFrom1 = iTo.getAnArgument() and - iFrom2 = iTo.getAnArgument() and - not predictableInstruction(iFrom1) and - not predictableInstruction(iFrom2) and - iFrom1 != iFrom2 - ) + exists(Variable checkedVar, Operand access | + /* + * This node is guarded by a condition that forces the accessed variable + * to equal something else. For example: + * ``` + * x = taintsource() + * if (x == 10) { + * taintsink(x); // not considered tainted + * } + * ``` + */ + + nodeIsBarrierEqualityCandidate(node, access, checkedVar) and + readsVariable(access.getDef(), checkedVar) + ) + } + + cached + predicate nodeIsBarrierIn(DataFlow::Node node) { + // don't use dataflow into taint sources, as this leads to duplicate results. + exists(Expr source | isUserInput(source, _) | + node = DataFlow::exprNode(source) + or + // This case goes together with the similar (but not identical) rule in + // `getNodeForSource`. + node = DataFlow::definitionByReferenceNodeFromArgument(source) + ) + or + // don't use dataflow into binary instructions if both operands are unpredictable + exists(BinaryInstruction iTo | + iTo = node.asInstruction() and + not predictableInstruction(iTo.getLeft()) and + not predictableInstruction(iTo.getRight()) and + // propagate taint from either the pointer or the offset, regardless of predictability + not iTo instanceof PointerArithmeticInstruction + ) + or + // don't use dataflow through calls to pure functions if two or more operands + // are unpredictable + exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | + iTo = node.asInstruction() and + isPureFunction(iTo.getStaticCallTarget().getName()) and + iFrom1 = iTo.getAnArgument() and + iFrom2 = iTo.getAnArgument() and + not predictableInstruction(iFrom1) and + not predictableInstruction(iFrom2) and + iFrom1 != iFrom2 + ) + } + + cached + Element adjustedSink(DataFlow::Node sink) { + // TODO: is it more appropriate to use asConvertedExpr here and avoid + // `getConversion*`? Or will that cause us to miss some cases where there's + // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to + // pretend there was flow to the converted `Expr` for the sake of + // compatibility. + sink.asExpr().getConversion*() = result + or + // For compatibility, send flow from arguments to parameters, even for + // functions with no body. + exists(FunctionCall call, int i | + sink.asExpr() = call.getArgument(i) and + result = resolveCall(call).getParameter(i) + ) + or + // For compatibility, send flow into a `Variable` if there is flow to any + // Load or Store of that variable. + exists(CopyInstruction copy | + copy.getSourceValue() = sink.asInstruction() and + ( + readsVariable(copy, result) or + writesVariable(copy, result) + ) and + not hasUpperBoundsCheck(result) + ) + or + // For compatibility, send flow into a `NotExpr` even if it's part of a + // short-circuiting condition and thus might get skipped. + result.(NotExpr).getOperand() = sink.asExpr() + or + // Taint postfix and prefix crement operations when their operand is tainted. + result.(CrementOperation).getAnOperand() = sink.asExpr() + or + // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. + result.(AssignOperation).getAnOperand() = sink.asExpr() + or + result = + sink.asOperand() + .(SideEffectOperand) + .getUse() + .(ReadSideEffectInstruction) + .getArgumentDef() + .getUnconvertedResultExpression() + } + + /** + * Step to return value of a modeled function when an input taints the + * dereference of the return value. + */ + cached + predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | + n1.asOperand() = callInput(call, modelIn) and + ( + func.(TaintFunction).hasTaintFlow(modelIn, modelOut) + or + func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) + ) and + call.getStaticCallTarget() = func and + modelOut.isReturnValueDeref() and + call = n2.asInstruction() + ) + } } -private Element adjustedSink(DataFlow::Node sink) { - // TODO: is it more appropriate to use asConvertedExpr here and avoid - // `getConversion*`? Or will that cause us to miss some cases where there's - // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to - // pretend there was flow to the converted `Expr` for the sake of - // compatibility. - sink.asExpr().getConversion*() = result - or - // For compatibility, send flow from arguments to parameters, even for - // functions with no body. - exists(FunctionCall call, int i | - sink.asExpr() = call.getArgument(i) and - result = resolveCall(call).getParameter(i) - ) - or - // For compatibility, send flow into a `Variable` if there is flow to any - // Load or Store of that variable. - exists(CopyInstruction copy | - copy.getSourceValue() = sink.asInstruction() and - ( - readsVariable(copy, result) or - writesVariable(copy, result) - ) and - not hasUpperBoundsCheck(result) - ) - or - // For compatibility, send flow into a `NotExpr` even if it's part of a - // short-circuiting condition and thus might get skipped. - result.(NotExpr).getOperand() = sink.asExpr() - or - // Taint postfix and prefix crement operations when their operand is tainted. - result.(CrementOperation).getAnOperand() = sink.asExpr() - or - // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. - result.(AssignOperation).getAnOperand() = sink.asExpr() - or - result = - sink.asOperand() - .(SideEffectOperand) - .getUse() - .(ReadSideEffectInstruction) - .getArgumentDef() - .getUnconvertedResultExpression() -} +private import Cached /** * Holds if `tainted` may contain taint from `source`. @@ -402,19 +429,7 @@ module TaintedWithPath { readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable)) ) or - // Step to return value of a modeled function when an input taints the - // dereference of the return value - exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | - n1.asOperand() = callInput(call, modelIn) and - ( - func.(TaintFunction).hasTaintFlow(modelIn, modelOut) - or - func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) - ) and - call.getStaticCallTarget() = func and - modelOut.isReturnValueDeref() and - call = n2.asInstruction() - ) + additionalTaintStep(n1, n2) } override predicate isSanitizer(DataFlow::Node node) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index e927634fec2..99d8555f8ca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -2,11 +2,14 @@ private import cpp private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import DataFlowImplCommon as DataFlowImplCommon /** * Gets a function that might be called by `call`. */ +cached Function viableCallable(CallInstruction call) { + DataFlowImplCommon::forceCachingInSameStage() and result = call.getStaticCallTarget() or // If the target of the call does not have a body in the snapshot, it might @@ -43,7 +46,6 @@ private module VirtualDispatch { abstract DataFlow::Node getDispatchValue(); /** Gets a candidate target for this call. */ - cached abstract Function resolve(); /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index f5fb7309cff..8ed61da4c92 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -12,10 +12,20 @@ private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.DataFlow cached -private newtype TIRDataFlowNode = - TInstructionNode(Instruction i) or - TOperandNode(Operand op) or - TVariableNode(Variable var) +private module Cached { + cached + newtype TIRDataFlowNode = + TInstructionNode(Instruction i) or + TOperandNode(Operand op) or + TVariableNode(Variable var) + + cached + predicate localFlowStepCached(Node nodeFrom, Node nodeTo) { + simpleLocalFlowStep(nodeFrom, nodeTo) + } +} + +private import Cached /** * A node in a data flow graph. @@ -590,7 +600,7 @@ Node uninitializedNode(LocalVariable v) { none() } * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. */ -predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) } +predicate localFlowStep = localFlowStepCached/2; /** * INTERNAL: do not use. @@ -598,7 +608,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Operand -> Instruction flow simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) From c35a2b959af56993a7cc659d09323a3328b62959 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Apr 2021 11:37:17 +0200 Subject: [PATCH 179/550] Python: Update data-flow caching --- .../dataflow/new/internal/DataFlowPrivate.qll | 2 - .../new/internal/TaintTrackingPrivate.qll | 62 ++++++++++--------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 9b0a8267270..a6640f41dc0 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -228,7 +228,6 @@ module EssaFlow { * data flow. It is a strict subset of the `localFlowStep` predicate, as it * excludes SSA flow through instance fields. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // If there is ESSA-flow out of a node `node`, we want flow // both out of `node` and any post-update node of `node`. @@ -1559,7 +1558,6 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -cached predicate clearsContent(Node n, Content c) { exists(CallNode call, CallableValue callable, string name | call_unpacks(call, _, callable, name, _) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll b/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll index f12d97d2fa5..a6e169243db 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll @@ -9,36 +9,42 @@ private import semmle.python.dataflow.new.internal.TaintTrackingPublic */ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } -/** - * Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all - * global taint flow configurations. - */ -predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - localAdditionalTaintStep(nodeFrom, nodeTo) - or - any(AdditionalTaintStep a).step(nodeFrom, nodeTo) +private module Cached { + /** + * Holds if the additional step from `nodeFrom` to `nodeTo` should be included in all + * global taint flow configurations. + */ + cached + predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + localAdditionalTaintStep(nodeFrom, nodeTo) + or + any(AdditionalTaintStep a).step(nodeFrom, nodeTo) + } + + /** + * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding + * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent + * different objects. + */ + cached + predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + concatStep(nodeFrom, nodeTo) + or + subscriptStep(nodeFrom, nodeTo) + or + stringManipulation(nodeFrom, nodeTo) + or + containerStep(nodeFrom, nodeTo) + or + copyStep(nodeFrom, nodeTo) + or + forStep(nodeFrom, nodeTo) + or + unpackingAssignmentStep(nodeFrom, nodeTo) + } } -/** - * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding - * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent - * different objects. - */ -predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - concatStep(nodeFrom, nodeTo) - or - subscriptStep(nodeFrom, nodeTo) - or - stringManipulation(nodeFrom, nodeTo) - or - containerStep(nodeFrom, nodeTo) - or - copyStep(nodeFrom, nodeTo) - or - forStep(nodeFrom, nodeTo) - or - unpackingAssignmentStep(nodeFrom, nodeTo) -} +import Cached /** * Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to concatenation. From 213d011a8cfcd3179c7db3922fbe259ddc48df0d Mon Sep 17 00:00:00 2001 From: jorgectf Date: Thu, 29 Apr 2021 11:10:03 +0200 Subject: [PATCH 180/550] Edit code example in CompiledRegex Signed-off-by: jorgectf --- .../src/experimental/semmle/python/frameworks/Stdlib.qll | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 9bda2cb3158..05ea4f62630 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -52,12 +52,13 @@ private module Re { * * ```py * pattern = re.compile(input) - * input.match(s) + * pattern.match(s) * ``` * - * `patternCall` refers to `re.compile(input)`, - * `regexNode` refers to `input` and - * `this` will refer to `input.match(s)` + * This class will identify that `re.compile` compiles `input` and afterwards + * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` + * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) + * * * See `RegexExecutionMethods` * From bd4b1893733ccdff3bc4ef564d03a15400e9b00b Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:26:28 +0200 Subject: [PATCH 181/550] Polish documentation consistency Co-authored-by: yoff --- python/ql/src/experimental/semmle/python/Concepts.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index e11e21da914..0661ebc5890 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -36,7 +36,7 @@ module RegexExecution { } /** - * A data-flow node that collect methods immediately executing an expression. + * A data-flow node that executes a regular expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexExecution::Range` instead. @@ -54,7 +54,7 @@ class RegexExecution extends DataFlow::Node { /** Provides classes for modeling Regular Expression escape-related APIs. */ module RegexEscape { /** - * A data-flow node that collects functions escaping regular expressions. + * A data-flow node that escapes a regular expression. * * Extend this class to model new APIs. If you want to refine existing API models, * extend `RegexEscape` instead. @@ -68,7 +68,7 @@ module RegexEscape { } /** - * A data-flow node that collects functions escaping regular expressions. + * A data-flow node that escapes a regular expression. * * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexEscape::Range` instead. From 8c3980d80b88e56db5c01265a9b2ca2c86bff197 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:54:43 +0300 Subject: [PATCH 182/550] Update cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- .../CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c index b09971b5328..53a50841977 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -3,7 +3,7 @@ while(intIndex > 2) ... intIndex--; ... -} // GOOD: coreten cycle +} // GOOD: correct cycle ... while(intIndex > 2) { From 0935c5a0f2a2fa2404eac279823ca2faecf6ab30 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:58:30 +0300 Subject: [PATCH 183/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index a253a5e0599..9acc1d35d81 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -26,7 +26,7 @@ class DangerousWhileLoop extends WhileStmt { exp = this.getCondition().getAChild*() and not exp instanceof PointerFieldAccess and not exp instanceof ValueFieldAccess and - exp.toString() = dl.getName() and + exp.(VariableAccess).getTarget().getName() = dl.getName() and not exp.getParent*() instanceof CrementOperation and not exp.getParent*() instanceof Assignment and not exp.getParent*() instanceof FunctionCall From 21f43252e6d48cc3638690225e762fc092c75f99 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:59:04 +0300 Subject: [PATCH 184/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.expected --- .../DeclarationOfVariableWithUnnecessarilyWideScope.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected index 7b540a33384..4b29dad8779 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -1 +1 @@ -| test.c:12:9:12:16 | intIndex | A variable with this name is used in the loop condition. | +| test.c:13:9:13:16 | intIndex | A variable with this name is used in the loop condition. | From bb97507ebc195ecdb43126c4fcab6741b274791e Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 2 May 2021 22:59:56 +0300 Subject: [PATCH 185/550] Update test.c --- .../Security/CWE/CWE-1126/semmle/tests/test.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c index 090bed34d45..325c278f697 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -6,10 +6,27 @@ void workFunction_0(char *s) { buf[intIndex] = 1; intIndex--; } + intIndex = 10; while(intIndex > 2) { buf[intIndex] = 1; int intIndex; // BAD intIndex--; } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex -= 2; + int intIndex; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + --intIndex; + int intIndex; + intIndex--; + } } From 4709e8139d88ff445352716fdc6177e8a4995d70 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 3 May 2021 01:43:56 +0000 Subject: [PATCH 186/550] JPython code injection --- .../CWE/CWE-094/JPythonInjection.java | 48 ++++ .../CWE/CWE-094/JPythonInjection.qhelp | 34 +++ .../Security/CWE/CWE-094/JPythonInjection.ql | 68 +++++ .../CWE-094/JPythonInjection.expected | 11 + .../security/CWE-094/JPythonInjection.java | 64 +++++ .../security/CWE-094/JPythonInjection.qlref | 1 + .../query-tests/security/CWE-094/options | 2 +- .../jpython-2.7.2/org/python/core/PyCode.java | 43 +++ .../org/python/core/PyException.java | 12 + .../org/python/core/PyObject.java | 11 + .../org/python/core/PySystemState.java | 177 ++++++++++++ .../org/python/core/ThreadState.java | 28 ++ .../org/python/util/PythonInterpreter.java | 252 ++++++++++++++++++ 13 files changed, 750 insertions(+), 1 deletion(-) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java create mode 100644 java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java new file mode 100644 index 00000000000..13db6830a71 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java @@ -0,0 +1,48 @@ +public class JPythonInjection extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + // BAD: allow arbitrary JPython expression to execute + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + // BAD: allow arbitrary JPython expression to evaluate + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp new file mode 100644 index 00000000000..dddbb2d618a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp @@ -0,0 +1,34 @@ + + + + +

    Python has been the most widely used programming language in recent years, and JPython + is a popular Java implementation of Python. It allows embedded Python scripting inside + Java applications and provides an interactive interpreter that can be used to interact + with Java packages or with running Java applications. If an expression is built using + attacker-controlled data and then evaluated, it may allow the attacker to run arbitrary + code.

    +
    + + +

    In general, including user input in JPython expression should be avoided. If user input + must be included in an expression, it should be then evaluated in a safe context that + doesn't allow arbitrary code invocation.

    +
    + + +

    The following code could execute random code in JPython Interpreter

    + +
    + + +
  • + JPython Organization: JPython and Java Integration +
  • +
  • + PortSwigger: Python code injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql new file mode 100644 index 00000000000..a6621d89c26 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql @@ -0,0 +1,68 @@ +/** + * @name Injection in JPython + * @description Evaluation of a user-controlled malicious expression in JPython + * may lead to remote code execution. + * @kind path-problem + * @id java/jpython-injection + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** The class `org.python.util.PythonInterpreter`. */ +class PythonInterpreter extends RefType { + PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } +} + +/** A method that evaluates, compiles or executes a JPython expression. */ +class InterpretExprMethod extends Method { + InterpretExprMethod() { + this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and + ( + hasName("exec") or + hasName("eval") or + hasName("compile") + ) + } +} + +/** Holds if a JPython expression if evaluated, compiled or executed. */ +predicate runCode(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof InterpretExprMethod and + sink = ma.getArgument(0) + ) +} + +/** Sink of an expression interpreted by JPython interpreter. */ +class CodeInjectionSink extends DataFlow::ExprNode { + CodeInjectionSink() { runCode(_, this.getExpr()) } + + MethodAccess getMethodAccess() { runCode(result, this.getExpr()) } +} + +class CodeInjectionConfiguration extends TaintTracking::Configuration { + CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); + exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "JPython evaluate $@.", + source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected new file mode 100644 index 00000000000..f5816001939 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected @@ -0,0 +1,11 @@ +edges +| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | +| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | +nodes +| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JPythonInjection.java:30:28:30:31 | code | semmle.label | code | +| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JPythonInjection.java:52:40:52:43 | code | semmle.label | code | +#select +| JPythonInjection.java:30:11:30:32 | exec(...) | JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | JPython evaluate $@. | JPythonInjection.java:22:23:22:50 | getParameter(...) | user input | +| JPythonInjection.java:52:23:52:44 | eval(...) | JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | JPython evaluate $@. | JPythonInjection.java:47:21:47:48 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java new file mode 100644 index 00000000000..a0515eb4212 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java @@ -0,0 +1,64 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.python.core.PyObject; +import org.python.core.PyException; +import org.python.util.PythonInterpreter; + +public class JPythonInjection extends HttpServlet { + private static final long serialVersionUID = 1L; + + public JPythonInjection() { + super(); + } + + // BAD: allow arbitrary JPython expression to execute + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + // BAD: allow arbitrary JPython expression to evaluate + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} + diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref new file mode 100644 index 00000000000..80217a193bd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/JPythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options index 18e3518fc97..ccf3a24f215 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/options +++ b/java/ql/test/experimental/query-tests/security/CWE-094/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jpython-2.7.2 diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java new file mode 100644 index 00000000000..9b7c99f94fa --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java @@ -0,0 +1,43 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +/** + * A super class for all python code implementations. + */ +public abstract class PyCode extends PyObject +{ + abstract public PyObject call(ThreadState state, + PyObject args[], String keywords[], + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject self, PyObject args[], + String keywords[], + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject globals, + PyObject[] defaults, PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, PyObject arg3, + PyObject globals, PyObject[] defaults, + PyObject closure); + + abstract public PyObject call(ThreadState state, + PyObject arg1, PyObject arg2, PyObject arg3, PyObject arg4, + PyObject globals, PyObject[] defaults, + PyObject closure); + +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java new file mode 100644 index 00000000000..3a0a6c52c69 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java @@ -0,0 +1,12 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; +import java.io.*; + +/** + * A wrapper for all python exception. Note that the well-known python exceptions are not + * subclasses of PyException. Instead the python exception class is stored in the type + * field and value or class instance is stored in the value field. + */ +public class PyException extends RuntimeException +{ +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java new file mode 100644 index 00000000000..00993123461 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java @@ -0,0 +1,11 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.Serializable; + +/** + * All objects known to the Jython runtime system are represented by an instance of the class + * {@code PyObject} or one of its subclasses. + */ +public class PyObject implements Serializable { +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java new file mode 100644 index 00000000000..8444bbba70e --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java @@ -0,0 +1,177 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/** + * The "sys" module. + */ +// xxx Many have lamented, this should really be a module! +// but it will require some refactoring to see this wish come true. +public class PySystemState extends PyObject { + public PySystemState() { + } + + public static void classDictInit(PyObject dict) { + } + + public ClassLoader getSyspathJavaLoader() { + return null; + } + + // xxx fix this accessors + public PyObject __findattr_ex__(String name) { + return null; + } + + public void __setattr__(String name, PyObject value) { + } + + public void __delattr__(String name) { + } + + public PyObject gettrace() { + return null; + } + + public void settrace(PyObject tracefunc) { + } + + /** + * Change the current working directory to the specified path. + * + * path is assumed to be absolute and canonical (via os.path.realpath). + * + * @param path a path String + */ + public void setCurrentWorkingDir(String path) { + } + + /** + * Return a string representing the current working directory. + * + * @return a path String + */ + public String getCurrentWorkingDir() { + return null; + } + + /** + * Resolve a path. Returns the full path taking the current working directory into account. + * + * @param path a path String + * @return a resolved path String + */ + public String getPath(String path) { + return null; + } + + /** + * Resolve a path, returning a {@link File}, taking the current working directory into account. + * + * @param path a path String + * @return a resolved File + */ + public File getFile(String path) { + return null; + } + + public ClassLoader getClassLoader() { + return null; + } + + public void setClassLoader(ClassLoader classLoader) { + } + + public static Properties getBaseProperties() { + return null; + } + + public static synchronized void initialize() { + } + + public static synchronized void initialize(Properties preProperties, + Properties postProperties) { + } + + public static synchronized void initialize(Properties preProperties, Properties postProperties, + String[] argv) { + } + + public static synchronized void initialize(Properties preProperties, Properties postProperties, + String[] argv, ClassLoader classLoader) { + } + + /** + * Add a classpath directory to the list of places that are searched for java packages. + *

    + * Note. Classes found in directory and sub-directory are not made available to jython by + * this call. It only makes the java package found in the directory available. This call is + * mostly useful if jython is embedded in an application that deals with its own class loaders. + * A servlet container is a very good example. Calling + * {@code add_classdir("/WEB-INF/classes")} makes the java packages in WEB-INF classes + * available to jython import. However the actual class loading is completely handled by the + * servlet container's context classloader. + */ + public static void add_classdir(String directoryPath) { + } + + /** + * Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip + * files. The .jar and .zip files found will not be cached. + *

    + * Note. Classes in .jar and .zip files found in the directory are not made available to + * jython by this call. See the note for add_classdir(dir) for more details. + * + * @param directoryPath The name of a directory. + * + * @see #add_classdir + */ + public static void add_extdir(String directoryPath) { + } + + /** + * Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip + * files. + *

    + * Note. Classes in .jar and .zip files found in the directory are not made available to + * jython by this call. See the note for add_classdir(dir) for more details. + * + * @param directoryPath The name of a directory. + * @param cache Controls if the packages in the zip and jar file should be cached. + * + * @see #add_classdir + */ + public static void add_extdir(String directoryPath, boolean cache) { + } + + // Not public by design. We can't rebind the displayhook if + // a reflected function is inserted in the class dict. + + /** + * Exit a Python program with the given status. + * + * @param status the value to exit with + * @throws PyException {@code SystemExit} always throws this exception. When caught at top level + * the program will exit. + */ + public static void exit(PyObject status) { + } + + /** + * Exit a Python program with the status 0. + */ + public static void exit() { + } + + public static void exc_clear() { + } + + public void cleanup() { + } + + public void close() { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java b/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java new file mode 100644 index 00000000000..920270fe053 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java @@ -0,0 +1,28 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +// a ThreadState refers to one PySystemState; this weak ref allows for tracking all ThreadState objects +// that refer to a given PySystemState + +public class ThreadState { + + public PyException exception; + + public ThreadState(PySystemState systemState) { + setSystemState(systemState); + } + + public void setSystemState(PySystemState systemState) { + } + + public PySystemState getSystemState() { + return null; + } + + public boolean enterRepr(PyObject obj) { + return false; + } + + public void exitRepr(PyObject obj) { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java b/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java new file mode 100644 index 00000000000..92c50917b59 --- /dev/null +++ b/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java @@ -0,0 +1,252 @@ +package org.python.util; + +import java.io.Closeable; +import java.io.Reader; +import java.io.StringReader; +import java.util.Properties; + +import org.python.core.PyCode; +import org.python.core.PyObject; + +/** + * The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a + * Java application. + */ +public class PythonInterpreter implements Closeable { + + /** + * Initializes the Jython runtime. This should only be called once, before any other Python + * objects (including PythonInterpreter) are created. + * + * @param preProperties A set of properties. Typically System.getProperties() is used. + * preProperties override properties from the registry file. + * @param postProperties Another set of properties. Values like python.home, python.path and all + * other values from the registry files can be added to this property set. + * postProperties override system properties and registry properties. + * @param argv Command line arguments, assigned to sys.argv. + */ + public static void + initialize(Properties preProperties, Properties postProperties, String[] argv) { + } + + /** + * Creates a new interpreter with an empty local namespace. + */ + public PythonInterpreter() { + } + + /** + * Creates a new interpreter with the ability to maintain a separate local namespace for each + * thread (set by invoking setLocals()). + * + * @param dict a Python mapping object (e.g., a dictionary) for use as the default namespace + */ + public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) { + return null; + } + + /** + * Creates a new interpreter with a specified local namespace. + * + * @param dict a Python mapping object (e.g., a dictionary) for use as the namespace + */ + public PythonInterpreter(PyObject dict) { + } + + /** + * Sets a Python object to use for the standard output stream, sys.stdout. This + * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object. + * The behaviour as implemented is: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Stream behaviour for various object types
    Python type of object o written
    str/bytesunicodeAny other type
    {@link PyFile}as bytes directlyrespect {@link PyFile#encoding}call str(o) first
    {@link PyFileWriter}each byte value as a charwrite as Java charscall o.toString() first
    Other {@link PyObject} finvoke f.write(str(o))invoke f.write(o)invoke f.write(str(o))
    + * + * @param outStream Python file-like object to use as the output stream + */ + public void setOut(PyObject outStream) { + } + + /** + * Sets a {@link java.io.Writer} to use for the standard output stream, sys.stdout. + * The behaviour as implemented is to output each object o by calling + * o.toString() and writing this as UTF-16. + * + * @param outStream to use as the output stream + */ + public void setOut(java.io.Writer outStream) { + } + + /** + * Sets a {@link java.io.OutputStream} to use for the standard output stream. + * + * @param outStream OutputStream to use as output stream + */ + public void setOut(java.io.OutputStream outStream) { + } + + /** + * Sets a Python object to use for the standard output stream, sys.stderr. This + * stream is used in a byte-oriented way (mostly) that depends on the type of file-like object, + * in the same way as {@link #setOut(PyObject)}. + * + * @param outStream Python file-like object to use as the error output stream + */ + public void setErr(PyObject outStream) { + } + + /** + * Sets a {@link java.io.Writer} to use for the standard output stream, sys.stdout. + * The behaviour as implemented is to output each object o by calling + * o.toString() and writing this as UTF-16. + * + * @param outStream to use as the error output stream + */ + public void setErr(java.io.Writer outStream) { + } + + public void setErr(java.io.OutputStream outStream) { + } + + /** + * Evaluates a string as a Python expression and returns the result. + */ + public PyObject eval(String s) { + return null; + } + + /** + * Evaluates a Python code object and returns the result. + */ + public PyObject eval(PyObject code) { + return null; + } + + /** + * Executes a string of Python source in the local namespace. + * + * In some environments, such as Windows, Unicode characters in the script will be converted + * into ascii question marks (?). This can be avoided by first compiling the fragment using + * PythonInterpreter.compile(), and using the overridden form of this method which takes a + * PyCode object. Code page declarations are not supported. + */ + public void exec(String s) { + } + + /** + * Executes a Python code object in the local namespace. + */ + public void exec(PyObject code) { + } + + /** + * Executes a file of Python source in the local namespace. + */ + public void execfile(String filename) { + } + + public void execfile(java.io.InputStream s) { + } + + public void execfile(java.io.InputStream s, String name) { + } + + /** + * Compiles a string of Python source as either an expression (if possible) or a module. + * + * Designed for use by a JSR 223 implementation: "the Scripting API does not distinguish between + * scripts which return values and those which do not, nor do they make the corresponding + * distinction between evaluating or executing objects." (SCR.4.2.1) + */ + public PyCode compile(String script) { + return null; + } + + public PyCode compile(Reader reader) { + return null; + } + + public PyCode compile(String script, String filename) { + return null; + } + + public PyCode compile(Reader reader, String filename) { + return null; + } + + /** + * Sets a variable in the local namespace. + * + * @param name the name of the variable + * @param value the object to set the variable to (as converted to an appropriate Python object) + */ + public void set(String name, Object value) { + } + + /** + * Sets a variable in the local namespace. + * + * @param name the name of the variable + * @param value the Python object to set the variable to + */ + public void set(String name, PyObject value) { + } + + /** + * Returns the value of a variable in the local namespace. + * + * @param name the name of the variable + * @return the value of the variable, or null if that name isn't assigned + */ + public PyObject get(String name) { + return null; + } + + /** + * Returns the value of a variable in the local namespace. + * + * The value will be returned as an instance of the given Java class. + * interp.get("foo", Object.class) will return the most appropriate generic Java + * object. + * + * @param name the name of the variable + * @param javaclass the class of object to return + * @return the value of the variable as the given class, or null if that name isn't assigned + */ + public T get(String name, Class javaclass) { + return null; + } + + public void cleanup() { + } + + public void close() { + } +} From 633f228dc2d444584128b7cfdb348b82512fde2e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 29 Apr 2021 11:48:28 +0200 Subject: [PATCH 187/550] C#: Add CFG tests for partial classes --- .../controlflow/graph/BasicBlock.expected | 2 + .../controlflow/graph/Dominance.expected | 48 +++++++++++++++++++ .../graph/EnclosingCallable.expected | 26 ++++++++++ .../controlflow/graph/EntryElement.expected | 12 +++++ .../controlflow/graph/ExitElement.expected | 12 +++++ .../controlflow/graph/NodeGraph.expected | 22 +++++++++ .../controlflow/graph/Nodes.expected | 2 + .../graph/PartialImplementationA.cs | 4 ++ .../graph/PartialImplementationB.cs | 6 +++ 9 files changed, 134 insertions(+) create mode 100644 csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs create mode 100644 csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 2ec16a930fb..dc77c10b22d 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -930,6 +930,8 @@ | NullCoalescing.cs:15:31:15:31 | 0 | NullCoalescing.cs:16:17:16:18 | "" | 5 | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:17:13:17:19 | (...) ... | 5 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | exit M6 | 4 | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | exit Partial | 12 | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | exit Partial | 12 | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:18:8:23 | Int32 i1 | 8 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | 1 | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | 1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 5a316e90ec0..72cebfe5621 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -3058,6 +3058,28 @@ dominance | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:9:17:24 | ... = ... | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:13:17:19 | (...) ... | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | exit Partial | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | exit Partial | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | exit M1 | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:7:9:7:24 | ... ...; | @@ -7130,6 +7152,28 @@ postDominance | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:19 | (...) ... | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:9:17:25 | ...; | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | this access | | Patterns.cs:5:10:5:11 | exit M1 | Patterns.cs:5:10:5:11 | exit M1 (normal) | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:40:17:40:17 | access to local variable o | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:5:10:5:11 | enter M1 | @@ -11677,6 +11721,8 @@ blockDominance | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:16:17:16:25 | ... ?? ... | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | enter Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | enter M1 | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:13:8:23 | [false] ... is ... | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:13:8:23 | [true] ... is ... | @@ -15218,6 +15264,8 @@ postBlockDominance | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:15:31:15:31 | 0 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:16:17:16:25 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | enter Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | enter M1 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index c9a2116762d..fe42f87ad26 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -3498,6 +3498,30 @@ nodeEnclosing | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:13:10:13:11 | M6 | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:4:12:4:18 | Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:5:10:5:11 | exit M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | M1 | @@ -5796,6 +5820,8 @@ blockEnclosing | NullCoalescing.cs:15:31:15:31 | 0 | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:5:10:5:11 | M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index d48906f29c5..4a8935d09b5 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -2158,6 +2158,18 @@ | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:24:17:24 | 1 | NullCoalescing.cs:17:24:17:24 | 1 | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationB.cs:3:16:3:16 | access to field F | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:34:5:34 | 0 | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:7:9:7:24 | ... ...; | Patterns.cs:7:9:7:24 | ... ...; | | Patterns.cs:7:16:7:23 | Object o = ... | Patterns.cs:7:20:7:23 | null | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index c96b19b371b..0207253e3f9 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -2826,6 +2826,18 @@ | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | normal | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:19:17:19 | access to parameter i | normal | | NullCoalescing.cs:17:24:17:24 | 1 | NullCoalescing.cs:17:24:17:24 | 1 | normal | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | normal | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:27:3:29 | {...} | normal | +| PartialImplementationB.cs:3:16:3:16 | access to field F | PartialImplementationB.cs:3:16:3:16 | this access | normal | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:16:3:16 | this access | normal | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:16:3:20 | ... = ... | normal | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:20:3:20 | 0 | normal | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | normal | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:22:4:24 | {...} | normal | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:16:5:16 | this access | normal | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:16:5:16 | this access | normal | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:32:5:34 | ... = ... | normal | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:34:5:34 | 0 | normal | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:40:17:40:17 | access to local variable o | normal | | Patterns.cs:7:9:7:24 | ... ...; | Patterns.cs:7:16:7:23 | Object o = ... | normal | | Patterns.cs:7:16:7:23 | Object o = ... | Patterns.cs:7:16:7:23 | Object o = ... | normal | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index c39b233b314..a637ecdd987 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -3524,6 +3524,28 @@ | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | semmle.label | non-null | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:9:17:24 | ... = ... | semmle.label | successor | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:13:17:19 | (...) ... | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | exit Partial | semmle.label | successor | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | exit Partial | semmle.label | successor | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | semmle.label | successor | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:27:3:29 | {...} | semmle.label | successor | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:22:4:24 | {...} | semmle.label | successor | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | semmle.label | successor | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | semmle.label | successor | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:6:5:43:5 | {...} | semmle.label | successor | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | exit M1 | semmle.label | successor | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:7:9:7:24 | ... ...; | semmle.label | successor | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected index b58f1a57cc7..8b158822831 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -1219,6 +1219,8 @@ entryPoint | NullCoalescing.cs:9:12:9:13 | M4 | NullCoalescing.cs:9:37:9:37 | access to parameter b | | NullCoalescing.cs:11:9:11:10 | M5 | NullCoalescing.cs:11:44:11:45 | access to parameter b1 | | NullCoalescing.cs:13:10:13:11 | M6 | NullCoalescing.cs:14:5:18:5 | {...} | +| PartialImplementationA.cs:3:12:3:18 | Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationB.cs:4:12:4:18 | Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | | Patterns.cs:5:10:5:11 | M1 | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:47:24:47:25 | M2 | Patterns.cs:48:9:48:9 | access to parameter c | | Patterns.cs:50:24:50:25 | M3 | Patterns.cs:51:9:51:9 | access to parameter c | diff --git a/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs new file mode 100644 index 00000000000..e3061f38a6e --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs @@ -0,0 +1,4 @@ +partial class Partial +{ + public Partial(int i) { } +} diff --git a/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs new file mode 100644 index 00000000000..c5120b44cdd --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs @@ -0,0 +1,6 @@ +partial class Partial +{ + public int F = 0; + public Partial() { } + public int P { get; set; } = 0; +} From 182b2d0457d7607c5f9d8a28ca165ff3b954d2cc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Apr 2021 09:10:25 +0200 Subject: [PATCH 188/550] C#: Improve CFG for constructors when there are multiple implementations --- .../internal/ControlFlowGraphImpl.qll | 73 ++++++++++------- .../csharp/controlflow/internal/Splitting.qll | 80 ++++++++++--------- .../controlflow/graph/BasicBlock.expected | 20 ++--- .../controlflow/graph/Consistency.expected | 24 ------ .../controlflow/graph/Dominance.expected | 64 +++++---------- .../graph/EnclosingCallable.expected | 16 ---- .../controlflow/graph/NodeGraph.expected | 16 ---- 7 files changed, 112 insertions(+), 181 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index a0c0784c010..6a5944db3a2 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -49,6 +49,27 @@ private import SuccessorType private import SuccessorTypes private import Splitting private import semmle.code.csharp.ExprOrStmtParent +private import semmle.code.csharp.commons.Compilation + +/** + * A compilation. + * + * Unlike the standard `Compilation` class, this class also supports buildless + * extraction. + */ +newtype CompilationExt = + TCompilation(Compilation c) { not extractionIsStandalone() } or + TBuildless() { extractionIsStandalone() } + +/** Gets the compilation that source file `f` belongs to. */ +CompilationExt getCompilation(SourceFile f) { + exists(Compilation c | + f = c.getAFileCompiled() and + result = TCompilation(c) + ) + or + result = TBuildless() +} /** An element that defines a new CFG scope. */ class CfgScope extends Element, @top_level_exprorstmt_parent { @@ -135,10 +156,7 @@ predicate scopeFirst(CfgScope scope, ControlFlowElement first) { then first(c.(Constructor).getInitializer(), first) else if InitializerSplitting::constructorInitializes(c, _) - then - first(any(InitializerSplitting::InitializedInstanceMember m | - InitializerSplitting::constructorInitializeOrder(c, m, 0) - ).getInitializer(), first) + then first(InitializerSplitting::constructorInitializeOrder(c, _, 0), first) else first(c.getBody(), first) ) or @@ -152,36 +170,36 @@ predicate scopeLast(Callable scope, ControlFlowElement last, Completion c) { last(scope.getBody(), last, c) and not c instanceof GotoCompletion or - exists(InitializerSplitting::InitializedInstanceMember m | - m = InitializerSplitting::lastConstructorInitializer(scope) and - last(m.getInitializer(), last, c) and - not scope.hasBody() - ) + last(InitializerSplitting::lastConstructorInitializer(scope, _), last, c) and + not scope.hasBody() } -private class CallableTree extends ControlFlowTree, Callable { +private class ConstructorTree extends ControlFlowTree, Constructor { final override predicate propagatesAbnormal(ControlFlowElement child) { none() } final override predicate first(ControlFlowElement first) { none() } final override predicate last(ControlFlowElement last, Completion c) { none() } + /** Gets the body of this constructor belonging to compilation `comp`. */ + pragma[noinline] + ControlFlowElement getBody(CompilationExt comp) { + result = this.getBody() and + comp = getCompilation(result.getFile()) + } + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - exists(Constructor con, InitializerSplitting::InitializedInstanceMember m, int i | - this = con and - last(m.getInitializer(), pred, c) and - c instanceof NormalCompletion and - InitializerSplitting::constructorInitializeOrder(con, m, i) + exists(CompilationExt comp, int i, AssignExpr ae | + ae = InitializerSplitting::constructorInitializeOrder(this, comp, i) and + last(ae, pred, c) and + c instanceof NormalCompletion | // Flow from one member initializer to the next - exists(InitializerSplitting::InitializedInstanceMember next | - InitializerSplitting::constructorInitializeOrder(con, next, i + 1) and - first(next.getInitializer(), succ) - ) + first(InitializerSplitting::constructorInitializeOrder(this, comp, i + 1), succ) or // Flow from last member initializer to constructor body - m = InitializerSplitting::lastConstructorInitializer(con) and - first(con.getBody(), succ) + ae = InitializerSplitting::lastConstructorInitializer(this, comp) and + first(this.getBody(comp), succ) ) } } @@ -884,21 +902,18 @@ module Expressions { first(this.getChildElement(i + 1), succ) ) or - exists(Constructor con | + exists(ConstructorTree con, CompilationExt comp | last(this, pred, c) and con = this.getConstructor() and + comp = getCompilation(this.getFile()) and c instanceof NormalCompletion | // Flow from constructor initializer to first member initializer - exists(InitializerSplitting::InitializedInstanceMember m | - InitializerSplitting::constructorInitializeOrder(con, m, 0) - | - first(m.getInitializer(), succ) - ) + first(InitializerSplitting::constructorInitializeOrder(con, comp, 0), succ) or // Flow from constructor initializer to first element of constructor body - not InitializerSplitting::constructorInitializeOrder(con, _, _) and - first(con.getBody(), succ) + not exists(InitializerSplitting::constructorInitializeOrder(con, comp, _)) and + first(con.getBody(comp), succ) ) } } diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll index 498e6c461ae..8826ee7c1be 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -218,23 +218,23 @@ module InitializerSplitting { * A non-static member with an initializer, for example a field `int Field = 0`. */ class InitializedInstanceMember extends Member { - private AssignExpr ae; - InitializedInstanceMember() { - not this.isStatic() and - expr_parent_top_level_adjusted(ae, _, this) and - not ae = any(Callable c).getExpressionBody() + exists(AssignExpr ae | + not this.isStatic() and + expr_parent_top_level(ae, _, this) and + not ae = any(Callable c).getExpressionBody() + ) } /** Gets the initializer expression. */ - AssignExpr getInitializer() { result = ae } + AssignExpr getInitializer() { expr_parent_top_level(result, _, this) } /** * Gets a control flow element that is a syntactic descendant of the * initializer expression. */ ControlFlowElement getAnInitializerDescendant() { - result = ae + result = this.getInitializer() or result = this.getAnInitializerDescendant().getAChild() } @@ -242,35 +242,41 @@ module InitializerSplitting { /** * Holds if `c` is a non-static constructor that performs the initialization - * of member `m`. + * of a member via assignment `init`. */ - predicate constructorInitializes(InstanceConstructor c, InitializedInstanceMember m) { - c.isUnboundDeclaration() and - c.getDeclaringType().getAMember() = m and - not c.getInitializer().isThis() + predicate constructorInitializes(InstanceConstructor c, AssignExpr init) { + exists(InitializedInstanceMember m | + c.isUnboundDeclaration() and + c.getDeclaringType().getAMember() = m and + not c.getInitializer().isThis() and + init = m.getInitializer() + ) } /** - * Holds if `m` is the `i`th member initialized by non-static constructor `c`. + * Gets the `i`th member initializer expression for non-static constructor `c` + * in compilation `comp`. */ - predicate constructorInitializeOrder(Constructor c, InitializedInstanceMember m, int i) { - constructorInitializes(c, m) and - m = - rank[i + 1](InitializedInstanceMember m0 | - constructorInitializes(c, m0) + AssignExpr constructorInitializeOrder(Constructor c, CompilationExt comp, int i) { + constructorInitializes(c, result) and + result = + rank[i + 1](AssignExpr ae0, Location l | + constructorInitializes(c, ae0) and + l = ae0.getLocation() and + getCompilation(l.getFile()) = comp | - m0 - order by - m0.getLocation().getStartLine(), m0.getLocation().getStartColumn(), - m0.getLocation().getFile().getAbsolutePath() + ae0 order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath() ) } - /** Gets the last member initialized by non-static constructor `c`. */ - InitializedInstanceMember lastConstructorInitializer(Constructor c) { + /** + * Gets the last member initializer expression for non-static constructor `c` + * in compilation `comp`. + */ + AssignExpr lastConstructorInitializer(Constructor c, CompilationExt comp) { exists(int i | - constructorInitializeOrder(c, result, i) and - not constructorInitializeOrder(c, _, i + 1) + result = constructorInitializeOrder(c, comp, i) and + not exists(constructorInitializeOrder(c, comp, i + 1)) ) } @@ -379,8 +385,9 @@ module InitializerSplitting { this.appliesTo(pred) and succ(pred, succ, c) and succ = - any(InitializedInstanceMember m | constructorInitializes(this.getConstructor(), m)) - .getAnInitializerDescendant() + any(InitializedInstanceMember m | + constructorInitializes(this.getConstructor(), m.getInitializer()) + ).getAnInitializerDescendant() } } } @@ -1204,14 +1211,12 @@ module BooleanSplitting { exists(Callable c, int r | c = kind.getEnclosingCallable() | result = r + ExceptionHandlerSplitting::getNextListOrder() - 1 and kind = - rank[r](BooleanSplitSubKind kind0 | + rank[r](BooleanSplitSubKind kind0, Location l | kind0.getEnclosingCallable() = c and - kind0.startsSplit(_) + kind0.startsSplit(_) and + l = kind0.getLocation() | - kind0 - order by - kind0.getLocation().getStartLine(), kind0.getLocation().getStartColumn(), - kind0.toString() + kind0 order by l.getStartLine(), l.getStartColumn(), kind0.toString() ) ) } @@ -1430,10 +1435,11 @@ module LoopSplitting { exists(Callable c, int r | c = enclosingCallable(loop) | result = r + BooleanSplitting::getNextListOrder() - 1 and loop = - rank[r](AnalyzableLoopStmt loop0 | - enclosingCallable(loop0) = c + rank[r](AnalyzableLoopStmt loop0, Location l | + enclosingCallable(loop0) = c and + l = loop0.getLocation() | - loop0 order by loop0.getLocation().getStartLine(), loop0.getLocation().getStartColumn() + loop0 order by l.getStartLine(), l.getStartColumn() ) ) } diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index dc77c10b22d..45b1b481612 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -752,7 +752,6 @@ | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | exit M (abnormal) | 3 | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | 3 | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:20 | ... = ... | 3 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | 2 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | 2 | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | @@ -776,19 +775,17 @@ | MultiImplementationA.cs:16:17:16:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 | 2 | | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | 2 | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | exit M2 | 4 | -| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | 1 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | 14 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | 14 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | 6 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | 6 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | 2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | 2 | -| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | 2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | 1 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:27:21:29 | {...} | 3 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | @@ -801,7 +798,6 @@ | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion (normal) | 2 | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | 2 | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:32:24:34 | ... = ... | 4 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 5 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 5 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | @@ -835,7 +831,6 @@ | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | 2 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:20 | ... = ... | 3 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | 1 | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | 1 | @@ -859,19 +854,17 @@ | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 | 2 | | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | 2 | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | exit M2 | 5 | -| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | 1 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | 12 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | 12 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | 4 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | 4 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | 2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | 2 | -| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | 2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | 1 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:27:19:29 | {...} | 3 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | @@ -884,7 +877,6 @@ | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion (abnormal) | 3 | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (abnormal) | 3 | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:32:22:34 | ... = ... | 4 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 5 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 5 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected index 0df56b8f207..bccab8e85a4 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected @@ -5,27 +5,3 @@ breakInvariant3 breakInvariant4 breakInvariant5 multipleSuccessors -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 72cebfe5621..de10c0acf6f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -2862,6 +2862,7 @@ dominance | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | | MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | @@ -2885,6 +2886,7 @@ dominance | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:21:18:21 | 0 | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:9:18:22 | exit M2 | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:29 | ...; | @@ -2897,6 +2899,7 @@ dominance | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | @@ -2908,6 +2911,7 @@ dominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | | MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | @@ -2945,6 +2949,7 @@ dominance | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | | MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | @@ -2970,6 +2975,7 @@ dominance | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | MultiImplementationB.cs:16:9:16:31 | exit M2 | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:30:18:33 | null | @@ -2980,6 +2986,7 @@ dominance | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | @@ -2994,6 +3001,7 @@ dominance | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | @@ -6969,6 +6977,7 @@ postDominance | MultiImplementationA.cs:8:16:8:16 | exit M (abnormal) | MultiImplementationA.cs:8:23:8:32 | throw ... | | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | MultiImplementationB.cs:5:23:5:23 | 2 | | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:20:13:20 | 0 | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | enter get_Item | @@ -6993,10 +7002,11 @@ postDominance | MultiImplementationA.cs:18:9:18:22 | exit M2 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:21:18:21 | 0 | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:24:18:34 | throw ...; | | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | MultiImplementationA.cs:20:24:20:28 | ... = ... | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:24:32:24:34 | ... = ... | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:29 | ...; | | MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:28:20:28 | access to parameter i | | MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:22:20:31 | {...} | @@ -7006,6 +7016,7 @@ postDominance | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 (abnormal) | MultiImplementationB.cs:20:13:20:23 | throw ...; | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 (normal) | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | @@ -7015,6 +7026,7 @@ postDominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:13:16:13:20 | ... = ... | | MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | access to property P | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | @@ -7050,6 +7062,7 @@ postDominance | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | MultiImplementationB.cs:5:23:5:23 | 2 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:20:11:20 | 1 | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | exit get_Item (abnormal) | MultiImplementationB.cs:12:31:12:40 | throw ... | @@ -7074,6 +7087,7 @@ postDominance | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:9:16:31 | enter M2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:24:18:34 | throw ...; | | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | MultiImplementationA.cs:20:24:20:28 | ... = ... | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:30:18:33 | null | | MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | @@ -7081,6 +7095,7 @@ postDominance | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 (abnormal) | MultiImplementationB.cs:20:13:20:23 | throw ...; | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 (normal) | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:19:20:22 | null | @@ -7089,6 +7104,7 @@ postDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | MultiImplementationA.cs:23:50:23:53 | null | | MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:56:21:59 | null | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:11:16:11:20 | ... = ... | | MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | access to property P | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | @@ -11408,7 +11424,6 @@ blockDominance | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | @@ -11447,33 +11462,23 @@ blockDominance | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -11492,7 +11497,6 @@ blockDominance | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -11541,7 +11545,6 @@ blockDominance | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | @@ -11580,33 +11583,23 @@ blockDominance | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | enter M2 | | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -11625,7 +11618,6 @@ blockDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -14999,7 +14991,6 @@ postBlockDominance | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | enter get_Item | @@ -15034,31 +15025,21 @@ postBlockDominance | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | enter C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | enter C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | @@ -15073,7 +15054,6 @@ postBlockDominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -15110,7 +15090,6 @@ postBlockDominance | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | @@ -15145,19 +15124,15 @@ postBlockDominance | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | @@ -15168,7 +15143,6 @@ postBlockDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index fe42f87ad26..ab190057a83 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -5628,8 +5628,6 @@ blockEnclosing | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | M | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | get_Item | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | get_Item | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | @@ -5661,16 +5659,12 @@ blockEnclosing | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationB.cs:19:12:19:13 | C2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | @@ -5683,8 +5677,6 @@ blockEnclosing | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | @@ -5718,8 +5710,6 @@ blockEnclosing | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | M | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | @@ -5751,16 +5741,12 @@ blockEnclosing | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:12:19:13 | C2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | @@ -5773,8 +5759,6 @@ blockEnclosing | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index a637ecdd987..2441e36f62d 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -3211,10 +3211,7 @@ | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | semmle.label | exception(NullReferenceException) | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | semmle.label | successor | | MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | | MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | semmle.label | successor | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | semmle.label | successor | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | semmle.label | successor | @@ -3251,7 +3248,6 @@ | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:9:18:22 | exit M2 | semmle.label | successor | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | @@ -3269,7 +3265,6 @@ | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | semmle.label | successor | | MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | semmle.label | successor | | MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | semmle.label | successor | @@ -3292,9 +3287,6 @@ | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | semmle.label | successor | | MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | semmle.label | successor | | MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | semmle.label | successor | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | semmle.label | successor | @@ -3352,9 +3344,6 @@ | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | semmle.label | successor | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | semmle.label | successor | | MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | | MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | semmle.label | successor | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | semmle.label | successor | @@ -3393,7 +3382,6 @@ | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | MultiImplementationB.cs:16:9:16:31 | exit M2 | semmle.label | successor | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | semmle.label | exception(NullReferenceException) | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | semmle.label | successor | -| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | semmle.label | successor | @@ -3409,7 +3397,6 @@ | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | semmle.label | successor | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | semmle.label | successor | | MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | semmle.label | successor | @@ -3435,10 +3422,7 @@ | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | semmle.label | successor | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | semmle.label | successor | | MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | | MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | semmle.label | successor | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | semmle.label | successor | From dfad1fc7402cd28f2e90fc4c8c43fa1ce1a7b734 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:58:00 -0400 Subject: [PATCH 189/550] [Java] Add support for com.google.common.base.MoreObjects#firstNonNull --- java/change-notes/2021-05-03-guava-first-non-null.md | 2 ++ java/ql/src/semmle/code/java/frameworks/guava/Base.qll | 3 ++- .../ql/test/library-tests/frameworks/guava/TestBase.java | 6 ++++++ .../guava-30.0/com/google/common/base/MoreObjects.java | 9 +++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 java/change-notes/2021-05-03-guava-first-non-null.md create mode 100644 java/ql/test/stubs/guava-30.0/com/google/common/base/MoreObjects.java diff --git a/java/change-notes/2021-05-03-guava-first-non-null.md b/java/change-notes/2021-05-03-guava-first-non-null.md new file mode 100644 index 00000000000..3cd307d9455 --- /dev/null +++ b/java/change-notes/2021-05-03-guava-first-non-null.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increase coverage of the Guava framework by adding support for `com.google.common.base.MoreObjects#firstNonNull`. diff --git a/java/ql/src/semmle/code/java/frameworks/guava/Base.qll b/java/ql/src/semmle/code/java/frameworks/guava/Base.qll index 194d723caf5..04a97f79f53 100644 --- a/java/ql/src/semmle/code/java/frameworks/guava/Base.qll +++ b/java/ql/src/semmle/code/java/frameworks/guava/Base.qll @@ -35,7 +35,8 @@ private class GuavaBaseCsv extends SummaryModelCsv { "com.google.common.base;Splitter;false;splitToList;(CharSequence);;Argument[0];ReturnValue;taint", "com.google.common.base;Splitter;false;splitToStream;(CharSequence);;Argument[0];ReturnValue;taint", "com.google.common.base;Splitter$MapSplitter;false;split;(CharSequence);;Argument[0];ReturnValue;taint", - "com.google.common.base;Preconditions;false;checkNotNull;;;Argument[0];ReturnValue;value" + "com.google.common.base;Preconditions;false;checkNotNull;;;Argument[0];ReturnValue;value", + "com.google.common.base;MoreObjects;false;firstNonNull;;;Argument[0..1];ReturnValue;value" ] } } diff --git a/java/ql/test/library-tests/frameworks/guava/TestBase.java b/java/ql/test/library-tests/frameworks/guava/TestBase.java index 9ce907f4551..ad75f5cafbf 100644 --- a/java/ql/test/library-tests/frameworks/guava/TestBase.java +++ b/java/ql/test/library-tests/frameworks/guava/TestBase.java @@ -60,4 +60,10 @@ class TestBase { void test4() { sink(Preconditions.checkNotNull(taint())); // $numTaintFlow=1 } + + void test5() { + sink(MoreObjects.firstNonNull(taint(), taint())); // $numTaintFlow=2 + sink(MoreObjects.firstNonNull(null, taint())); // $numTaintFlow=1 + sink(MoreObjects.firstNonNull(taint(), null)); // $numTaintFlow=1 + } } diff --git a/java/ql/test/stubs/guava-30.0/com/google/common/base/MoreObjects.java b/java/ql/test/stubs/guava-30.0/com/google/common/base/MoreObjects.java new file mode 100644 index 00000000000..3f4912b021b --- /dev/null +++ b/java/ql/test/stubs/guava-30.0/com/google/common/base/MoreObjects.java @@ -0,0 +1,9 @@ +package com.google.common.base; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class MoreObjects { + public static T firstNonNull(@Nullable T first, @Nullable T second) { + return null; + } +} \ No newline at end of file From 703fbf139aaf828b1bd954f5b60714036a99df64 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 4 May 2021 02:54:49 +0000 Subject: [PATCH 190/550] Add more methods and update the library name --- .../CWE/CWE-094/JPythonInjection.java | 48 ------ .../CWE/CWE-094/JPythonInjection.qhelp | 34 ----- .../Security/CWE/CWE-094/JPythonInjection.ql | 68 --------- .../Security/CWE/CWE-094/JythonInjection.java | 47 ++++++ .../CWE/CWE-094/JythonInjection.qhelp | 34 +++++ .../Security/CWE/CWE-094/JythonInjection.ql | 123 +++++++++++++++ .../CWE-094/JPythonInjection.expected | 11 -- .../security/CWE-094/JPythonInjection.java | 64 -------- .../security/CWE-094/JPythonInjection.qlref | 1 - .../security/CWE-094/JythonInjection.expected | 21 +++ .../security/CWE-094/JythonInjection.java | 144 ++++++++++++++++++ .../security/CWE-094/JythonInjection.qlref | 1 + .../query-tests/security/CWE-094/options | 2 +- .../org/python/antlr/base/mod.java | 5 + .../org/python/core/BytecodeLoader.java | 47 ++++++ .../org/python/core/CompileMode.java | 11 ++ .../org/python/core/CompilerFlags.java | 17 +++ .../jython-2.7.2/org/python/core/Py.java | 134 ++++++++++++++++ .../org/python/core/PyCode.java | 0 .../org/python/core/PyException.java | 0 .../org/python/core/PyObject.java | 0 .../org/python/core/PySystemState.java | 0 .../org/python/core/ThreadState.java | 0 .../python/util/InteractiveInterpreter.java | 114 ++++++++++++++ .../org/python/util/PythonInterpreter.java | 0 25 files changed, 699 insertions(+), 227 deletions(-) delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyCode.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyException.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PyObject.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/PySystemState.java (100%) rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/core/ThreadState.java (100%) create mode 100644 java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java rename java/ql/test/stubs/{jpython-2.7.2 => jython-2.7.2}/org/python/util/PythonInterpreter.java (100%) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java deleted file mode 100644 index 13db6830a71..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.java +++ /dev/null @@ -1,48 +0,0 @@ -public class JPythonInjection extends HttpServlet { - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - try { - interpreter = new PythonInterpreter(); - interpreter.setOut(out); - interpreter.setErr(out); - - // BAD: allow arbitrary JPython expression to execute - interpreter.exec(code); - out.flush(); - - response.getWriter().print(out.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - out.close(); - } - } - - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - - try { - interpreter = new PythonInterpreter(); - // BAD: allow arbitrary JPython expression to evaluate - PyObject py = interpreter.eval(code); - - response.getWriter().print(py.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - } - } -} - diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp deleted file mode 100644 index dddbb2d618a..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.qhelp +++ /dev/null @@ -1,34 +0,0 @@ - - - - -

    Python has been the most widely used programming language in recent years, and JPython - is a popular Java implementation of Python. It allows embedded Python scripting inside - Java applications and provides an interactive interpreter that can be used to interact - with Java packages or with running Java applications. If an expression is built using - attacker-controlled data and then evaluated, it may allow the attacker to run arbitrary - code.

    - - - -

    In general, including user input in JPython expression should be avoided. If user input - must be included in an expression, it should be then evaluated in a safe context that - doesn't allow arbitrary code invocation.

    -
    - - -

    The following code could execute random code in JPython Interpreter

    - -
    - - -
  • - JPython Organization: JPython and Java Integration -
  • -
  • - PortSwigger: Python code injection -
  • -
    -
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql deleted file mode 100644 index a6621d89c26..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JPythonInjection.ql +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @name Injection in JPython - * @description Evaluation of a user-controlled malicious expression in JPython - * may lead to remote code execution. - * @kind path-problem - * @id java/jpython-injection - * @tags security - * external/cwe/cwe-094 - * external/cwe/cwe-095 - */ - -import java -import semmle.code.java.dataflow.FlowSources -import DataFlow::PathGraph - -/** The class `org.python.util.PythonInterpreter`. */ -class PythonInterpreter extends RefType { - PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } -} - -/** A method that evaluates, compiles or executes a JPython expression. */ -class InterpretExprMethod extends Method { - InterpretExprMethod() { - this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and - ( - hasName("exec") or - hasName("eval") or - hasName("compile") - ) - } -} - -/** Holds if a JPython expression if evaluated, compiled or executed. */ -predicate runCode(MethodAccess ma, Expr sink) { - exists(Method m | m = ma.getMethod() | - m instanceof InterpretExprMethod and - sink = ma.getArgument(0) - ) -} - -/** Sink of an expression interpreted by JPython interpreter. */ -class CodeInjectionSink extends DataFlow::ExprNode { - CodeInjectionSink() { runCode(_, this.getExpr()) } - - MethodAccess getMethodAccess() { runCode(result, this.getExpr()) } -} - -class CodeInjectionConfiguration extends TaintTracking::Configuration { - CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) - } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf -where conf.hasFlowPath(source, sink) -select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "JPython evaluate $@.", - source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java new file mode 100644 index 00000000000..fca518443d1 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -0,0 +1,47 @@ +public class JythonInjection extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + // BAD: allow arbitrary Jython expression to execute + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + // BAD: allow arbitrary Jython expression to evaluate + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp new file mode 100644 index 00000000000..8916296f93b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp @@ -0,0 +1,34 @@ + + + + +

    Python has been the most widely used programming language in recent years, and Jython + (formerly known as JPython) is a popular Java implementation of Python. It allows + embedded Python scripting inside Java applications and provides an interactive interpreter + that can be used to interact with Java packages or with running Java applications. If an + expression is built using attacker-controlled data and then evaluated, it may allow the + attacker to run arbitrary code.

    +
    + + +

    In general, including user input in Jython expression should be avoided. If user input + must be included in an expression, it should be then evaluated in a safe context that + doesn't allow arbitrary code invocation.

    +
    + + +

    The following code could execute arbitrary code in Jython Interpreter

    + +
    + + +
  • + Jython Organization: Jython and Java Integration +
  • +
  • + PortSwigger: Python code injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql new file mode 100644 index 00000000000..088c33e00fd --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -0,0 +1,123 @@ +/** + * @name Injection in Jython + * @description Evaluation of a user-controlled malicious expression in Java Python + * interpreter may lead to remote code execution. + * @kind path-problem + * @id java/jython-injection + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** The class `org.python.util.PythonInterpreter`. */ +class PythonInterpreter extends RefType { + PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } +} + +/** A method that evaluates, compiles or executes a Jython expression. */ +class InterpretExprMethod extends Method { + InterpretExprMethod() { + this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and + ( + getName().matches("exec%") or + hasName("eval") or + hasName("compile") or + getName().matches("run%") + ) + } +} + +/** The class `org.python.core.BytecodeLoader`. */ +class BytecodeLoader extends RefType { + BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") } +} + +/** Holds if a Jython expression if evaluated, compiled or executed. */ +predicate runCode(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof InterpretExprMethod and + sink = ma.getArgument(0) + ) +} + +/** A method that loads Java class data. */ +class LoadClassMethod extends Method { + LoadClassMethod() { + this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and + ( + hasName("makeClass") or + hasName("makeCode") + ) + } +} + +/** Holds if a Java class file is loaded. */ +predicate loadClass(MethodAccess ma, Expr sink) { + exists(Method m, int i | m = ma.getMethod() | + m instanceof LoadClassMethod and + m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) + sink = ma.getArgument(i) + ) +} + +/** The class `org.python.core.Py`. */ +class Py extends RefType { + Py() { this.hasQualifiedName("org.python.core", "Py") } +} + +/** A method that compiles code with `Py`. */ +class PyCompileMethod extends Method { + PyCompileMethod() { + this.getDeclaringType().getAnAncestor*() instanceof Py and + getName().matches("compile%") + } +} + +/** Holds if source code is compiled with `PyCompileMethod`. */ +predicate compile(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof PyCompileMethod and + sink = ma.getArgument(0) + ) +} + +/** Sink of an expression loaded by Jython. */ +class CodeInjectionSink extends DataFlow::ExprNode { + CodeInjectionSink() { + runCode(_, this.getExpr()) or + loadClass(_, this.getExpr()) or + compile(_, this.getExpr()) + } + + MethodAccess getMethodAccess() { + runCode(result, this.getExpr()) or + loadClass(result, this.getExpr()) or + compile(result, this.getExpr()) + } +} + +class CodeInjectionConfiguration extends TaintTracking::Configuration { + CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + or + source instanceof LocalUserInput + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); + exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.", + source.getNode(), "user input" diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected deleted file mode 100644 index f5816001939..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.expected +++ /dev/null @@ -1,11 +0,0 @@ -edges -| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | -| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | -nodes -| JPythonInjection.java:22:23:22:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| JPythonInjection.java:30:28:30:31 | code | semmle.label | code | -| JPythonInjection.java:47:21:47:48 | getParameter(...) : String | semmle.label | getParameter(...) : String | -| JPythonInjection.java:52:40:52:43 | code | semmle.label | code | -#select -| JPythonInjection.java:30:11:30:32 | exec(...) | JPythonInjection.java:22:23:22:50 | getParameter(...) : String | JPythonInjection.java:30:28:30:31 | code | JPython evaluate $@. | JPythonInjection.java:22:23:22:50 | getParameter(...) | user input | -| JPythonInjection.java:52:23:52:44 | eval(...) | JPythonInjection.java:47:21:47:48 | getParameter(...) : String | JPythonInjection.java:52:40:52:43 | code | JPython evaluate $@. | JPythonInjection.java:47:21:47:48 | getParameter(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java deleted file mode 100644 index a0515eb4212..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.java +++ /dev/null @@ -1,64 +0,0 @@ -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.python.core.PyObject; -import org.python.core.PyException; -import org.python.util.PythonInterpreter; - -public class JPythonInjection extends HttpServlet { - private static final long serialVersionUID = 1L; - - public JPythonInjection() { - super(); - } - - // BAD: allow arbitrary JPython expression to execute - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - try { - interpreter = new PythonInterpreter(); - interpreter.setOut(out); - interpreter.setErr(out); - interpreter.exec(code); - out.flush(); - - response.getWriter().print(out.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - out.close(); - } - } - - // BAD: allow arbitrary JPython expression to evaluate - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/plain"); - String code = request.getParameter("code"); - PythonInterpreter interpreter = null; - - try { - interpreter = new PythonInterpreter(); - PyObject py = interpreter.eval(code); - - response.getWriter().print(py.toString()); - } catch(PyException ex) { - response.getWriter().println(ex.getMessage()); - } finally { - if (interpreter != null) { - interpreter.close(); - } - } - } -} - diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref deleted file mode 100644 index 80217a193bd..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JPythonInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-094/JPythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected new file mode 100644 index 00000000000..4f66cc83fbd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.expected @@ -0,0 +1,21 @@ +edges +| JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | +| JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | +| JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | +| JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | +nodes +| JythonInjection.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:36:30:36:33 | code | semmle.label | code | +| JythonInjection.java:53:23:53:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:58:44:58:47 | code | semmle.label | code | +| JythonInjection.java:73:23:73:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:81:35:81:38 | code | semmle.label | code | +| JythonInjection.java:97:23:97:50 | getParameter(...) : String | semmle.label | getParameter(...) : String | +| JythonInjection.java:106:61:106:75 | getBytes(...) | semmle.label | getBytes(...) | +| JythonInjection.java:131:40:131:63 | getInputStream(...) | semmle.label | getInputStream(...) | +#select +| JythonInjection.java:36:13:36:34 | exec(...) | JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | Jython evaluate $@. | JythonInjection.java:28:23:28:50 | getParameter(...) | user input | +| JythonInjection.java:58:27:58:48 | eval(...) | JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | Jython evaluate $@. | JythonInjection.java:53:23:53:50 | getParameter(...) | user input | +| JythonInjection.java:81:13:81:39 | runsource(...) | JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | Jython evaluate $@. | JythonInjection.java:73:23:73:50 | getParameter(...) | user input | +| JythonInjection.java:106:29:106:134 | makeCode(...) | JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | Jython evaluate $@. | JythonInjection.java:97:23:97:50 | getParameter(...) | user input | +| JythonInjection.java:131:29:131:109 | compile(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | Jython evaluate $@. | JythonInjection.java:131:40:131:63 | getInputStream(...) | user input | diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java new file mode 100644 index 00000000000..682e8af5113 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java @@ -0,0 +1,144 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.python.core.BytecodeLoader; +import org.python.core.Py; +import org.python.core.PyCode; +import org.python.core.PyException; +import org.python.core.PyObject; +import org.python.util.InteractiveInterpreter; +import org.python.util.PythonInterpreter; + +public class JythonInjection extends HttpServlet { + private static final long serialVersionUID = 1L; + + public JythonInjection() { + super(); + } + + // BAD: allow arbitrary Jython expression to execute + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + // BAD: allow arbitrary Jython expression to evaluate + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: allow arbitrary Jython expression to run + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + InteractiveInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new InteractiveInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + interpreter.runsource(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: load arbitrary class file to execute + protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + PyCode pyCode = BytecodeLoader.makeCode("test", code.getBytes(), getServletContext().getRealPath("/com/example/test.pyc")); + interpreter.exec(pyCode); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } + + // BAD: Compile Python code to execute + protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + PyCode pyCode = Py.compile(request.getInputStream(), "Test.py", org.python.core.CompileMode.eval); + interpreter.exec(pyCode); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref new file mode 100644 index 00000000000..0ba9fd60621 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-094/JythonInjection.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/options b/java/ql/test/experimental/query-tests/security/CWE-094/options index ccf3a24f215..95bc9acaa08 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/options +++ b/java/ql/test/experimental/query-tests/security/CWE-094/options @@ -1,2 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jpython-2.7.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jython-2.7.2 diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java b/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java new file mode 100644 index 00000000000..785212f62fa --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/antlr/base/mod.java @@ -0,0 +1,5 @@ +// Autogenerated AST node +package org.python.antlr.base; + +public abstract class mod { +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java new file mode 100644 index 00000000000..e414216ed03 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/BytecodeLoader.java @@ -0,0 +1,47 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.util.List; + +/** + * Utility class for loading compiled Python modules and Java classes defined in Python modules. + */ +public class BytecodeLoader { + + /** + * Turn the Java class file data into a Java class. + * + * @param name fully-qualified binary name of the class + * @param data a class file as a byte array + * @param referents super-classes and interfaces that the new class will reference. + */ + @SuppressWarnings("unchecked") + public static Class makeClass(String name, byte[] data, Class... referents) { + return null; + } + + /** + * Turn the Java class file data into a Java class. + * + * @param name the name of the class + * @param referents super-classes and interfaces that the new class will reference. + * @param data a class file as a byte array + */ + public static Class makeClass(String name, List> referents, byte[] data) { + return null; + } + + /** + * Turn the Java class file data for a compiled Python module into a {@code PyCode} object, by + * constructing an instance of the named class and calling the instance's + * {@link PyRunnable#getMain()}. + * + * @param name fully-qualified binary name of the class + * @param data a class file as a byte array + * @param filename to provide to the constructor of the named class + * @return the {@code PyCode} object produced by the named class' {@code getMain} + */ + public static PyCode makeCode(String name, byte[] data, String filename) { + return null; + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java new file mode 100644 index 00000000000..cf7ad1e7201 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompileMode.java @@ -0,0 +1,11 @@ +package org.python.core; + +public enum CompileMode { + eval, + single, + exec; + + public static CompileMode getMode(String mode) { + return null; + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java new file mode 100644 index 00000000000..916b93c84ea --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/CompilerFlags.java @@ -0,0 +1,17 @@ +// At some future point this will also be extended - in conjunction with +// Py#compileFlags - to add +// support for a compiler factory that user code can choose in place of the +// normal compiler. +// (Perhaps a better name might have been "CompilerOptions".) + +package org.python.core; + +import java.io.Serializable; + +public class CompilerFlags implements Serializable { + public CompilerFlags() { + } + + public CompilerFlags(int co_flags) { + } +} diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java new file mode 100644 index 00000000000..cc0c9f1e4bd --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java @@ -0,0 +1,134 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.core; + +import java.io.InputStream; +import java.io.Serializable; + +import org.python.antlr.base.mod; + +public final class Py { + /** + Convert a given PyObject to an instance of a Java class. + Identical to o.__tojava__(c) except that it will + raise a TypeError if the conversion fails. + @param o the PyObject to convert. + @param c the class to convert it to. + **/ + @SuppressWarnings("unchecked") + public static T tojava(PyObject o, Class c) { + return null; + } + + // ??pending: was @deprecated but is actually used by proxie code. + // Can get rid of it? + public static Object tojava(PyObject o, String s) { + return null; + } + + /** + * Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn o into a PyObject. + * + * @see ClassicPyObjectAdapter - default PyObjectAdapter type + */ + public static PyObject java2py(Object o) { + return null; + } + + /** + * Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn + * objects into an array of PyObjects. + * + * @see ClassicPyObjectAdapter - default PyObjectAdapter type + */ + public static PyObject[] javas2pys(Object... objects) { + return null; + } + + public static PyObject makeClass(String name, PyObject[] bases, PyCode code, + PyObject[] closure_cells) { + return null; + } + + public static PyObject makeClass(String name, PyObject base, PyObject dict) { + return null; + } + + /** + * Create a new Python class. + * + * @param name the String name of the class + * @param bases an array of PyObject base classes + * @param dict the class's namespace, containing the class body + * definition + * @return a new Python Class PyObject + */ + public static PyObject makeClass(String name, PyObject[] bases, PyObject dict) { + return null; + } + + public static CompilerFlags getCompilerFlags() { + return null; + } + + public static CompilerFlags getCompilerFlags(int flags, boolean dont_inherit) { + return null; + } + + public static CompilerFlags getCompilerFlags(CompilerFlags flags, boolean dont_inherit) { + return null; + } + + // w/o compiler-flags + public static PyCode compile(InputStream istream, String filename, CompileMode kind) { + return null; + } + + /** + * Entry point for compiling modules. + * + * @param node Module node, coming from the parsing process + * @param name Internal name for the compiled code. Typically generated by + * calling {@link #getName()}. + * @param filename Source file name + * @param linenumbers True to track source line numbers on the generated + * code + * @param printResults True to call the sys.displayhook on the result of + * the code + * @param cflags Compiler flags + * @return Code object for the compiled module + */ + public static PyCode compile_flags(mod node, String name, String filename, + boolean linenumbers, boolean printResults, + CompilerFlags cflags) { + return null; + } + + public static PyCode compile_flags(mod node, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + /** + * Compiles python source code coming from a file or another external stream + */ + public static PyCode compile_flags(InputStream istream, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + /** + * Compiles python source code coming from String (raw bytes) data. + * + * If the String is properly decoded (from PyUnicode) the PyCF_SOURCE_IS_UTF8 flag + * should be specified. + */ + public static PyCode compile_flags(String data, String filename, + CompileMode kind, CompilerFlags cflags) { + return null; + } + + public static PyObject compile_command_flags(String string, String filename, + CompileMode kind, CompilerFlags cflags, boolean stdprompt) { + return null; + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyCode.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyException.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyException.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyException.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PyObject.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PyObject.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PyObject.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/PySystemState.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/PySystemState.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/PySystemState.java diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java b/java/ql/test/stubs/jython-2.7.2/org/python/core/ThreadState.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/core/ThreadState.java rename to java/ql/test/stubs/jython-2.7.2/org/python/core/ThreadState.java diff --git a/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java b/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java new file mode 100644 index 00000000000..b12fa617227 --- /dev/null +++ b/java/ql/test/stubs/jython-2.7.2/org/python/util/InteractiveInterpreter.java @@ -0,0 +1,114 @@ +// Copyright (c) Corporation for National Research Initiatives +package org.python.util; + +import org.python.core.*; + +/** + * This class provides the interface for compiling and running code that supports an interactive + * interpreter. + */ +// Based on CPython-1.5.2's code module +public class InteractiveInterpreter extends PythonInterpreter { + + /** + * Construct an InteractiveInterpreter with all default characteristics: default state (from + * {@link Py#getSystemState()}), and a new empty dictionary of local variables. + * */ + public InteractiveInterpreter() { + } + + /** + * Construct an InteractiveInterpreter with state (from {@link Py#getSystemState()}), and the + * specified dictionary of local variables. + * + * @param locals dictionary to use, or if null, a new empty one will be created + */ + public InteractiveInterpreter(PyObject locals) { + } + + /** + * Construct an InteractiveInterpreter with, and system state the specified dictionary of local + * variables. + * + * @param locals dictionary to use, or if null, a new empty one will be created + * @param systemState interpreter state, or if null use {@link Py#getSystemState()} + */ + public InteractiveInterpreter(PyObject locals, PySystemState systemState) { + } + + /** + * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which + * is used for incremental compilation at the interactive console, known as {@code }. + * + * @param source Python code + * @return true to indicate a partial statement was entered + */ + public boolean runsource(String source) { + return false; + } + + /** + * Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which + * is used for incremental compilation at the interactive console. + * + * @param source Python code + * @param filename name with which to label this console input (e.g. in error messages). + * @return true to indicate a partial statement was entered + */ + public boolean runsource(String source, String filename) { + return false; + } + + /** + * Compile and run some source in the interpreter, according to the {@link CompileMode} given. + * This method supports incremental compilation and interpretation through the return value, + * where {@code true} signifies that more input is expected in order to complete the Python + * statement. An interpreter can use this to decide whether to use {@code sys.ps1} + * ("{@code >>> }") or {@code sys.ps2} ("{@code ... }") to prompt the next line. The arguments + * are the same as the mandatory ones in the Python {@code compile()} command. + *

    + * One the following can happen: + *

      + *
    1. The input is incorrect; compilation raised an exception (SyntaxError or OverflowError). A + * syntax traceback will be printed by calling {@link #showexception(PyException)}. Return is + * {@code false}.
    2. + * + *
    3. The input is incomplete, and more input is required; compilation returned no code. + * Nothing happens. Return is {@code true}.
    4. + * + *
    5. The input is complete; compilation returned a code object. The code is executed by + * calling {@link #runcode(PyObject)} (which also handles run-time exceptions, except for + * SystemExit). Return is {@code false}.
    6. + *
    + * + * @param source Python code + * @param filename name with which to label this console input (e.g. in error messages). + * @param kind of compilation required: {@link CompileMode#eval}, {@link CompileMode#exec} or + * {@link CompileMode#single} + * @return {@code true} to indicate a partial statement was provided + */ + public boolean runsource(String source, String filename, CompileMode kind) { + return false; + } + + /** + * Execute a code object. When an exception occurs, {@link #showexception(PyException)} is + * called to display a stack trace, except in the case of SystemExit, which is re-raised. + *

    + * A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not + * always be caught. The caller should be prepared to deal with it. + **/ + + // Make this run in another thread somehow???? + public void runcode(PyObject code) { + } + + public void showexception(PyException exc) { + } + + public void write(String data) { + } + + public void resetbuffer() { + } +} diff --git a/java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java b/java/ql/test/stubs/jython-2.7.2/org/python/util/PythonInterpreter.java similarity index 100% rename from java/ql/test/stubs/jpython-2.7.2/org/python/util/PythonInterpreter.java rename to java/ql/test/stubs/jython-2.7.2/org/python/util/PythonInterpreter.java From 616a57d6d40c159bbe186269a9153eb956c567b2 Mon Sep 17 00:00:00 2001 From: Felicity Chapman Date: Tue, 4 May 2021 11:24:19 +0100 Subject: [PATCH 191/550] Update article with code scanning example --- ...nalyzing-databases-with-the-codeql-cli.rst | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 60126d12d0a..6d30fee7f65 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -101,39 +101,44 @@ You can also run your own custom queries with the ``database analyze`` command. For more information about preparing your queries to use with the CodeQL CLI, see ":doc:`Using custom queries with the CodeQL CLI `." - -Running LGTM.com query suites -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Running GitHub code scanning suites +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The CodeQL repository also includes query suites, which can be run over your code as part of a broader code review. CodeQL query suites are ``.qls`` files that use directives to select queries to run based on certain metadata properties. -The query suites included in the CodeQL repository select the same set of -queries that are run by default on `LGTM.com `__. The queries -are selected to highlight the most relevant and useful results for each -language. - -The language-specific LGTM query suites are located at the following paths in +The CodeQL repository includes query suites that are used by the CodeQL action on +`GitHub.com `__. The query suites are located at the following paths in the CodeQL repository:: - ql//ql/src/codeql-suites/-lgtm.qls + ql//ql/src/codeql-suites/-code-scanning.qls and at the following path in the CodeQL for Go repository:: - ql/src/codeql-suites/go-lgtm.qls + ql/src/codeql-suites/go-code-scanning.qls These locations are specified in the metadata included in the standard QL packs. -This means that CodeQL knows where to find the suite files automatically, and +This means that the CodeQL CLI knows where to find the suite files automatically, and you don't have to specify the full path on the command line when running an analysis. For more information, see ":ref:`About QL packs `." -For example, to run the LGTM.com query suite on a C++ codebase (generating -results in the latest SARIF format), you would run:: +.. pull-quote:: + + Important + + If you plan to upload the results to GitHub, you must generate SARIF results. + For more information, see `Analyzing a CodeQL database `__ in the GitHub documentation. + +For example, to run the code scanning query suite on a C++ codebase and generate +results in the v2.1 SARIF format supported by all versions of GitHub, you would run:: + + codeql database analyze cpp-code-scanning.qls --format=sarifv2.1.0 --output=cpp-analysis/cpp-results.sarif + +The repository also includes the query suites used by `LGTM.com `__. +These are stored alongside the code scanning suites with names of the form: ``-lgtm.qls``. - codeql database analyze cpp-lgtm.qls --format=sarif-latest --output=cpp-analysis/cpp-results.sarif - For information about creating custom query suites, see ":doc:`Creating CodeQL query suites `." From dc4a0c1d38dfe3cc6daae92b4cdae98f133ac766 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 5 May 2021 10:13:54 +0200 Subject: [PATCH 192/550] Python/JS: Fix typo --- .../javascript/security/internal/SensitiveDataHeuristics.qll | 2 +- .../semmle/python/security/internal/SensitiveDataHeuristics.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index 592b6c23f29..ddf95b1b534 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -116,7 +116,7 @@ module HeuristicNames { * it is hashed or encrypted). `classification` describes the kind of sensitive data * involved. * - * That is, one of the rexeps from `maybeSensitiveRegexp` matches `name` (with the + * That is, one of the regexps from `maybeSensitiveRegexp` matches `name` (with the * given classification), and none of the regexps from `notSensitiveRegexp` matches * `name`. */ diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index 592b6c23f29..ddf95b1b534 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -116,7 +116,7 @@ module HeuristicNames { * it is hashed or encrypted). `classification` describes the kind of sensitive data * involved. * - * That is, one of the rexeps from `maybeSensitiveRegexp` matches `name` (with the + * That is, one of the regexps from `maybeSensitiveRegexp` matches `name` (with the * given classification), and none of the regexps from `notSensitiveRegexp` matches * `name`. */ From 955d97f6bed68939dd8e613ff265aabaa15b2e1c Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Wed, 5 May 2021 21:25:36 +0300 Subject: [PATCH 193/550] C++: Init SqlPqxxTainted.ql --- .../Security/CWE/CWE-089/SqlPqxxTainted.cpp | 28 +++ .../Security/CWE/CWE-089/SqlPqxxTainted.qhelp | 31 ++++ .../Security/CWE/CWE-089/SqlPqxxTainted.ql | 170 ++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp new file mode 100644 index 00000000000..3b85835fff9 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +int main(int argc, char ** argv) { + + if (argc != 2) { + throw std::runtime_error("Give me a string!"); + } + + pqxx::connection c; + pqxx::work w(c); + + // BAD + char *userName = argv[1]; + char query1[1000] = {0}; + sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName); + pqxx::row r = w.exec1(query1); + w.commit(); + std::cout << r[0].as() << std::endl; + + // GOOD + pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1])); + w.commit(); + std::cout << r2[0][0].c_str() << std::endl; + + return 0; +} diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp new file mode 100644 index 00000000000..e92015edecf --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp @@ -0,0 +1,31 @@ + + + +

    The code passes user input as part of a SQL query without escaping special elements. +It generates a SQL query to Postgres using sprintf, +with the user-supplied data directly passed as an argument +to sprintf. This leaves the code vulnerable to attack by SQL Injection.

    + + + + +

    Use a library routine to escape characters in the user-supplied +string before converting it to SQL. Use esc and quote pqxx library functions.

    + +
    + + + + + + +
  • MSDN Library: SQL Injection.
  • + + + + +
    + diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql new file mode 100644 index 00000000000..2481b723cac --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql @@ -0,0 +1,170 @@ +/** + * @name Uncontrolled data in SQL query to Postgres + * @description Including user-supplied data in a SQL query to Postgres + * without neutralizing special elements can make code + * vulnerable to SQL Injection. + * @kind path-problem + * @problem.severity error + * @precision high + * @id cpp/sql-injection + * @tags security + * external/cwe/cwe-089 + */ + +import cpp +import semmle.code.cpp.security.Security +// import semmle.code.cpp.security.FunctionWithWrappers +// import semmle.code.cpp.security.TaintTracking +// import TaintedWithPath +import semmle.code.cpp.dataflow.TaintTracking +import DataFlow::PathGraph + +predicate pqxxTransationClassNames(string class_name, string namespace) { + class_name = "dbtransaction" and namespace = "pqxx" + or + class_name = "nontransaction" and namespace = "pqxx" + or + class_name = "basic_robusttransaction" and namespace = "pqxx" + or + class_name = "robusttransaction" and namespace = "pqxx" + or + class_name = "subtransaction" and namespace = "pqxx" + or + class_name = "transaction" and namespace = "pqxx" + or + class_name = "basic_transaction" and namespace = "pqxx" + or + class_name = "transaction_base" and namespace = "pqxx" + or + class_name = "work" and namespace = "pqxx" +} + +predicate pqxxConnectionClassNames(string class_name, string namespace) { + class_name = "connection_base" and namespace = "pqxx" + or + class_name = "basic_connection" and namespace = "pqxx" + or + class_name = "connection" and namespace = "pqxx" +} + +predicate pqxxTransactionSqlArgument(string function, int arg) { + function = "exec" and arg = 0 + or + function = "exec0" and arg = 0 + or + function = "exec1" and arg = 0 + or + function = "exec_n" and arg = 1 + or + function = "exec_params" and arg = 0 + or + function = "exec_params0" and arg = 0 + or + function = "exec_params1" and arg = 0 + or + function = "exec_params_n" and arg = 1 + or + function = "query_value" and arg = 0 + or + function = "stream" and arg = 0 +} + +predicate pqxxConnectionSqlArgument(string function, int arg) { + function = "prepare" and arg = 1 +} + +Expr getPqxxSqlArgument() { + exists(FunctionCall fc, Expr e, int argIndex, Type t | + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transation exec and connection prepare variations + ( + pqxxTransationClassNames(t.getName(), _) and pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex) + or + pqxxConnectionClassNames(t.getName(), _) and pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex) + ) + and + result = fc.getArgument(argIndex) + ) +} + +predicate pqxxEscapeArgument(string function, int arg) { + arg = 0 and + ( + function = "esc" + or + function = "esc_raw" + or + function = "quote" + or + function = "quote_raw" + or + function = "quote_name" + or + function = "quote_table" + or + function = "esc_like" + ) +} + +predicate isEscapedPqxxArgument(Expr argExpr) { + exists(FunctionCall fc, Expr e, int argIndex, Type t | + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transation and connection escape functions + (pqxxTransationClassNames(t.getName(), _) or pqxxConnectionClassNames(t.getName(), _)) + and + pqxxEscapeArgument(fc.getTarget().getName(), argIndex) + and + // eval is escaped + argExpr = fc.getArgument(argIndex) + ) +} + +// class Configuration extends TaintTrackingConfiguration { +// override predicate isSink(Element tainted) { +// tainted = getPqxxSqlArgument() +// } +// override predicate isBarrier(Expr e) { +// super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType +// } +// } +// from +// Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode, string taintCause +// where +// taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and +// isUserInput(taintSource, taintCause) +// select taintedArg, sourceNode, sinkNode, +// "This argument to a SQL query function is derived from $@", taintSource, "user input (" + taintCause + ")" + + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "SqlPqxxTainted" } + + override predicate isSource(DataFlow::Node source) { + isUserInput(source.asExpr(), _) + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = getPqxxSqlArgument() + } + + override predicate isSanitizer(DataFlow::Node node) { + isEscapedPqxxArgument(node.asExpr()) + } +} + +from + DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause +where + config.hasFlowPath(source, sink) and + isUserInput(source.getNode().asExpr(), taintCause) +select + sink, source, sink, + "This argument to a SQL query function is derived from $@", source, "user input (" + taintCause + ")" From 330eaea467532fac4fc2501bf56de7f606c3b64f Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Wed, 5 May 2021 21:48:14 +0300 Subject: [PATCH 194/550] C++: SqlPqxxTainted.ql style fixes --- .../Security/CWE/CWE-089/SqlPqxxTainted.ql | 121 +++++++----------- 1 file changed, 45 insertions(+), 76 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql index 2481b723cac..9d27674409a 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql @@ -13,9 +13,6 @@ import cpp import semmle.code.cpp.security.Security -// import semmle.code.cpp.security.FunctionWithWrappers -// import semmle.code.cpp.security.TaintTracking -// import TaintedWithPath import semmle.code.cpp.dataflow.TaintTracking import DataFlow::PathGraph @@ -69,102 +66,74 @@ predicate pqxxTransactionSqlArgument(string function, int arg) { function = "stream" and arg = 0 } -predicate pqxxConnectionSqlArgument(string function, int arg) { - function = "prepare" and arg = 1 -} +predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 } Expr getPqxxSqlArgument() { exists(FunctionCall fc, Expr e, int argIndex, Type t | - // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. - e = fc.getQualifier() and - // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior - // and return pointer to a connection/transation object - e.getType().refersTo(t) and - // transation exec and connection prepare variations - ( - pqxxTransationClassNames(t.getName(), _) and pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex) - or - pqxxConnectionClassNames(t.getName(), _) and pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex) - ) - and - result = fc.getArgument(argIndex) + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transation exec and connection prepare variations + ( + pqxxTransationClassNames(t.getName(), _) and + pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex) + or + pqxxConnectionClassNames(t.getName(), _) and + pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex) + ) and + result = fc.getArgument(argIndex) ) } predicate pqxxEscapeArgument(string function, int arg) { arg = 0 and ( - function = "esc" - or - function = "esc_raw" - or - function = "quote" - or - function = "quote_raw" - or - function = "quote_name" - or - function = "quote_table" - or - function = "esc_like" + function = "esc" + or + function = "esc_raw" + or + function = "quote" + or + function = "quote_raw" + or + function = "quote_name" + or + function = "quote_table" + or + function = "esc_like" ) } predicate isEscapedPqxxArgument(Expr argExpr) { exists(FunctionCall fc, Expr e, int argIndex, Type t | - // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. - e = fc.getQualifier() and - // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior - // and return pointer to a connection/transation object - e.getType().refersTo(t) and - // transation and connection escape functions - (pqxxTransationClassNames(t.getName(), _) or pqxxConnectionClassNames(t.getName(), _)) - and - pqxxEscapeArgument(fc.getTarget().getName(), argIndex) - and - // eval is escaped - argExpr = fc.getArgument(argIndex) + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transation and connection escape functions + (pqxxTransationClassNames(t.getName(), _) or pqxxConnectionClassNames(t.getName(), _)) and + pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and + // eval is escaped + argExpr = fc.getArgument(argIndex) ) } -// class Configuration extends TaintTrackingConfiguration { -// override predicate isSink(Element tainted) { -// tainted = getPqxxSqlArgument() -// } -// override predicate isBarrier(Expr e) { -// super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType -// } -// } -// from -// Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode, string taintCause -// where -// taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and -// isUserInput(taintSource, taintCause) -// select taintedArg, sourceNode, sinkNode, -// "This argument to a SQL query function is derived from $@", taintSource, "user input (" + taintCause + ")" - - class Configuration extends TaintTracking::Configuration { Configuration() { this = "SqlPqxxTainted" } - override predicate isSource(DataFlow::Node source) { - isUserInput(source.asExpr(), _) - } + override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) } - override predicate isSink(DataFlow::Node sink) { - sink.asExpr() = getPqxxSqlArgument() - } + override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() } - override predicate isSanitizer(DataFlow::Node node) { - isEscapedPqxxArgument(node.asExpr()) - } + override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) } } -from - DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause -where +from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause +where config.hasFlowPath(source, sink) and isUserInput(source.getNode().asExpr(), taintCause) -select - sink, source, sink, - "This argument to a SQL query function is derived from $@", source, "user input (" + taintCause + ")" +select sink, source, sink, "This argument to a SQL query function is derived from $@", source, + "user input (" + taintCause + ")" From b277082462848f3b102fac3c4f79e9babe8b4048 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Wed, 5 May 2021 23:28:04 +0300 Subject: [PATCH 195/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.qhelp --- .../DeclarationOfVariableWithUnnecessarilyWideScope.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp index d84f47f5453..5234212f7ca 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp @@ -3,7 +3,7 @@ "qhelp.dtd"> -

    Using variables with the same name is dangerous. However, such a situation inside the while loop can lead to a violation of the accessibility of the program. Requires the attention of developers.

    +

    Using variables with the same name is dangerous. However, such a situation inside the while loop can create an infinite loop exhausting resources. Requires the attention of developers.

    From 976ccda135748f5fff12250eed60f02e14d9aab3 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Wed, 5 May 2021 23:34:21 +0300 Subject: [PATCH 196/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../DeclarationOfVariableWithUnnecessarilyWideScope.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index 9acc1d35d81..be712b1cb1d 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -1,7 +1,7 @@ /** * @name Errors When Using Variable Declaration Inside Loop * @description Using variables with the same name is dangerous. - * However, such a situation inside the while loop can lead to a violation of the accessibility of the program. + * However, such a situation inside the while loop can create an infinite loop exhausting resources. * Requires the attention of developers. * @kind problem * @id cpp/errors-when-using-variable-declaration-inside-loop @@ -37,7 +37,7 @@ class DangerousWhileLoop extends WhileStmt { /** Holds when there are changes to the variables involved in the condition. */ predicate isUseThisVariable() { exists(Variable v | - this.getCondition().getAChild*().(VariableAccess).getTarget() = v and + exp.(VariableAccess).getTarget() = v and ( exists(Assignment aexp | aexp = this.getStmt().getAChild*() and From 67e9f063047d5040f73f21f641270e07a8a3a144 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Wed, 5 May 2021 17:27:49 -0400 Subject: [PATCH 197/550] [Java] Fix Kryo FP & Kryo 5 Support Closes #4992 --- .../2021-05-05-kryo-improvements.md | 3 + .../code/java/dataflow/ExternalFlow.qll | 1 + .../src/semmle/code/java/frameworks/Kryo.qll | 55 ++++++++++++++++- .../java/security/UnsafeDeserialization.qll | 59 +++++++++++++++++++ .../security/CWE-502/KryoTest.java | 34 +++++++++++ .../kryo/pool/KryoCallback.java | 7 +++ .../kryo/pool/KryoFactory.java | 7 +++ .../esotericsoftware/kryo/pool/KryoPool.java | 30 ++++++++++ 8 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 java/change-notes/2021-05-05-kryo-improvements.md create mode 100644 java/ql/test/query-tests/security/CWE-502/KryoTest.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java create mode 100644 java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md new file mode 100644 index 00000000000..f8ba79eb863 --- /dev/null +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Add support for version 5 of the Kryo serialzation/deserialization framework. +* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder. (#4992)[https://github.com/github/codeql/issues/4992] diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index 7073c57ff9c..b62018dc2c4 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -291,6 +291,7 @@ private predicate summaryModelCsv(string row) { "java.util;StringTokenizer;false;StringTokenizer;;;Argument[0];Argument[-1];taint", "java.beans;XMLDecoder;false;XMLDecoder;;;Argument[0];Argument[-1];taint", "com.esotericsoftware.kryo.io;Input;false;Input;;;Argument[0];Argument[-1];taint", + "com.esotericsoftware.kryo5.io;Input;false;Input;;;Argument[0];Argument[-1];taint", "java.io;BufferedInputStream;false;BufferedInputStream;;;Argument[0];Argument[-1];taint", "java.io;DataInputStream;false;DataInputStream;;;Argument[0];Argument[-1];taint", "java.io;ByteArrayInputStream;false;ByteArrayInputStream;;;Argument[0];Argument[-1];taint", diff --git a/java/ql/src/semmle/code/java/frameworks/Kryo.qll b/java/ql/src/semmle/code/java/frameworks/Kryo.qll index 049be97ea9c..16873fc751f 100644 --- a/java/ql/src/semmle/code/java/frameworks/Kryo.qll +++ b/java/ql/src/semmle/code/java/frameworks/Kryo.qll @@ -3,19 +3,60 @@ */ import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSteps /** * The type `com.esotericsoftware.kryo.Kryo`. */ class Kryo extends RefType { - Kryo() { this.hasQualifiedName("com.esotericsoftware.kryo", "Kryo") } + Kryo() { + hasQualifiedName("com.esotericsoftware.kryo", "Kryo") or + hasQualifiedName("com.esotericsoftware.kryo5", "Kryo") + } } /** * A Kryo input stream. */ class KryoInput extends RefType { - KryoInput() { this.hasQualifiedName("com.esotericsoftware.kryo.io", "Input") } + KryoInput() { + hasQualifiedName("com.esotericsoftware.kryo.io", "Input") or + hasQualifiedName("com.esotericsoftware.kryo5.io", "Input") + } +} + +/** + * A Kryo pool. + */ +class KryoPool extends RefType { + KryoPool() { + hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool") or + hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool") + } +} + +/** + * A Kryo pool builder. + */ +class KryoPoolBuilder extends RefType { + KryoPoolBuilder() { + hasQualifiedName("com.esotericsoftware.kryo.pool", "KryoPool$Builder") or + hasQualifiedName("com.esotericsoftware.kryo5.pool", "KryoPool$Builder") + } +} + +/** + * A Kryo pool builder method used a fluent API call chain. + */ +class KryoPoolBuilderMethod extends Method { + KryoPoolBuilderMethod() { + getDeclaringType() instanceof KryoPoolBuilder and + ( + getReturnType() instanceof KryoPoolBuilder or + getReturnType() instanceof KryoPool + ) + } } /** @@ -45,3 +86,13 @@ class KryoEnableWhiteListing extends MethodAccess { ) } } + +/** + * A KryoPool method that uses a Kryo instance. + */ +class KryoPoolRunMethod extends Method { + KryoPoolRunMethod() { + getDeclaringType() instanceof KryoPool and + hasName("run") + } +} diff --git a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll index ab809f07d6d..def37c0964e 100644 --- a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll +++ b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll @@ -48,6 +48,65 @@ class SafeKryo extends DataFlow2::Configuration { ma.getMethod() instanceof KryoReadObjectMethod ) } + + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + stepKryoPoolBuilderFactoryArgToConstructor(node1, node2) or + stepKryoPoolRunMethodAccessQualifierToFunctionalArgument(node1, node2) or + stepKryoPoolBuilderChainMethod(node1, node2) or + stepKryoPoolBorrowMethod(node1, node2) + } + + /** + * Holds when a funcitonal expression is used to create a `KryoPool.Builder`. + * Eg. `new KryoPool.Builder(() -> new Kryo())` + */ + private predicate stepKryoPoolBuilderFactoryArgToConstructor( + DataFlow::Node node1, DataFlow::Node node2 + ) { + exists(ConstructorCall cc, FunctionalExpr fe | + cc.getConstructedType() instanceof KryoPoolBuilder and + fe.asMethod().getBody().getAStmt().(ReturnStmt).getResult() = node1.asExpr() and + node2.asExpr() = cc and + cc.getArgument(0) = fe + ) + } + + /** + * Holds when a `KryoPool.run` is called to use a `Kryo` instance. + * Eg. `pool.run(kryo -> ...)` + */ + private predicate stepKryoPoolRunMethodAccessQualifierToFunctionalArgument( + DataFlow::Node node1, DataFlow::Node node2 + ) { + exists(MethodAccess ma | + ma.getMethod() instanceof KryoPoolRunMethod and + node1.asExpr() = ma.getQualifier() and + ma.getArgument(0).(FunctionalExpr).asMethod().getParameter(0) = node2.asParameter() + ) + } + + /** + * Holds when a `KryoPool.Builder` method is called fluently. + */ + private predicate stepKryoPoolBuilderChainMethod(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma | + ma.getMethod() instanceof KryoPoolBuilderMethod and + ma = node2.asExpr() and + ma.getQualifier() = node1.asExpr() + ) + } + + /** + * Holds when a `KryoPool.borrow` method is called. + */ + private predicate stepKryoPoolBorrowMethod(DataFlow::Node node1, DataFlow::Node node2) { + exists(MethodAccess ma | + ma.getMethod() = + any(Method m | m.getDeclaringType() instanceof KryoPool and m.hasName("borrow")) and + node1.asExpr() = ma.getQualifier() and + node2.asExpr() = ma + ) + } } predicate unsafeDeserialization(MethodAccess ma, Expr sink) { diff --git a/java/ql/test/query-tests/security/CWE-502/KryoTest.java b/java/ql/test/query-tests/security/CWE-502/KryoTest.java new file mode 100644 index 00000000000..8890bad91b9 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-502/KryoTest.java @@ -0,0 +1,34 @@ + +import java.io.*; +import java.net.Socket; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.pool.KryoPool; +import com.esotericsoftware.kryo.io.Input; + +public class KryoTest { + + private Kryo getSafeKryo() { + Kryo kryo = new Kryo(); + kryo.setRegistrationRequired(true); + // ... kryo.register(A.class) ... + return kryo; + } + + public void kryoDeserialize(Socket sock) throws java.io.IOException { + KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build(); + Input input = new Input(sock.getInputStream()); + Object o = kryoPool.run(kryo -> kryo.readClassAndObject(input)); // OK + } + + public void kryoDeserialize2(Socket sock) throws java.io.IOException { + KryoPool kryoPool = new KryoPool.Builder(this::getSafeKryo).softReferences().build(); + Input input = new Input(sock.getInputStream()); + Kryo k = kryoPool.borrow(); + try { + Object o = k.readClassAndObject(input); // OK + } finally { + kryoPool.release(k); + } + } + +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java new file mode 100644 index 00000000000..729426aba62 --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoCallback.java @@ -0,0 +1,7 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; + +public interface KryoCallback { + T execute (Kryo kryo); +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java new file mode 100644 index 00000000000..4dcda1445df --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoFactory.java @@ -0,0 +1,7 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; + +public interface KryoFactory { + Kryo create (); +} diff --git a/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java new file mode 100644 index 00000000000..c005442c9e8 --- /dev/null +++ b/java/ql/test/stubs/kryo-4.0.2/com/esotericsoftware/kryo/pool/KryoPool.java @@ -0,0 +1,30 @@ +package com.esotericsoftware.kryo.pool; + +import com.esotericsoftware.kryo.Kryo; +import java.util.Queue; + +public interface KryoPool { + + Kryo borrow (); + + void release (Kryo kryo); + + T run (KryoCallback callback); + + static class Builder { + public Builder (KryoFactory factory) { + } + + public Builder queue (Queue queue) { + return null; + } + + public Builder softReferences () { + return null; + } + + public KryoPool build () { + return null; + } + } +} From a400a1e9d4cd4542767a8e78e7b2afb8c73fb4a3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 10:17:57 +0200 Subject: [PATCH 198/550] split the markdown steps into a separate class --- .../semmle/javascript/frameworks/Markdown.qll | 289 ++++++++++-------- 1 file changed, 155 insertions(+), 134 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll index 743341325a7..788e28e2e9e 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll @@ -2,159 +2,180 @@ * Provides classes for modelling common markdown parsers and generators. */ +import semmle.javascript.Unit import javascript /** - * A taint step for the `marked` library, that converts markdown to HTML. + * A module providing taint-steps for common markdown parsers and generators. */ -private class MarkedStep extends TaintTracking::SharedTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode call | - call = DataFlow::globalVarRef("marked").getACall() - or - call = DataFlow::moduleImport("marked").getACall() - | - succ = call and - pred = call.getArgument(0) - ) - } -} - -/** - * A taint step for the `markdown-table` library. - */ -private class MarkdownTableStep extends TaintTracking::SharedTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode call | call = DataFlow::moduleImport("markdown-table").getACall() | - succ = call and - pred = call.getArgument(0) - ) - } -} - -/** - * A taint step for the `showdown` library. - */ -private class ShowDownStep extends TaintTracking::SharedTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode call | - call = - [DataFlow::globalVarRef("showdown"), DataFlow::moduleImport("showdown")] - .getAConstructorInvocation("Converter") - .getAMemberCall(["makeHtml", "makeMd"]) - | - succ = call and - pred = call.getArgument(0) - ) - } -} - -/** - * Classes and predicates for modelling taint steps in `unified` and `remark`. - */ -private module Unified { +module Markdown { /** - * The creation of a parser from `unified`. - * The `remark` module is a shorthand that initializes `unified` with a markdown parser. + * A taint-step that parses a markdown document, but preserves taint.import */ - DataFlow::CallNode unified() { result = DataFlow::moduleImport(["unified", "remark"]).getACall() } - - /** - * A chain of method calls that process an input with `unified`. - */ - class UnifiedChain extends DataFlow::CallNode { - DataFlow::CallNode root; - - UnifiedChain() { - root = unified() and - this = root.getAChainedMethodCall(["process", "processSync"]) - } - - /** - * Gets a plugin that is used in this chain. - */ - DataFlow::Node getAUsedPlugin() { result = root.getAChainedMethodCall("use").getArgument(0) } - - /** - * Gets the input that is processed. - */ - DataFlow::Node getInput() { result = getArgument(0) } - - /** - * Gets the processed output. - */ - DataFlow::Node getOutput() { - this.getCalleeName() = "process" and result = getABoundCallbackParameter(1, 1) - or - this.getCalleeName() = "processSync" and result = this - } + class MarkdownStep extends Unit { + abstract predicate step(DataFlow::Node pred, DataFlow::Node succ); } - /** - * A taint step for the `unified` library. - */ - class UnifiedStep extends TaintTracking::SharedTaintStep { + private class MarkdownStepAsTaintStep extends TaintTracking::SharedTaintStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(UnifiedChain chain | - // sanitizer. Mostly looking for `rehype-sanitize`, but also other plugins with `sanitize` in their name. - not chain.getAUsedPlugin().getALocalSource() = - DataFlow::moduleImport(any(string s | s.matches("%sanitize%"))) - | - pred = chain.getInput() and - succ = chain.getOutput() - ) + any(MarkdownStep step).step(pred, succ) } } -} - -/** - * A taint step for the `snarkdown` library. - */ -private class SnarkdownStep extends TaintTracking::SharedTaintStep { - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode call | call = DataFlow::moduleImport("snarkdown").getACall() | - call = succ and - pred = call.getArgument(0) - ) - } -} - -/** - * Classes and predicates for modelling taint steps the `markdown-it` library. - */ -private module MarkdownIt { - /** - * The creation of a parser from `markdown-it`. - */ - private API::Node markdownIt() { - exists(API::InvokeNode call | - call = API::moduleImport("markdown-it").getAnInvocation() - or - call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation() - | - call.getParameter(0).getMember("html").getARhs().mayHaveBooleanValue(true) and - result = call.getReturn() - ) - or - exists(API::CallNode call | - call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and - result = call.getReturn() and - not call.getParameter(0).getAValueReachingRhs() = - DataFlow::moduleImport("markdown-it-sanitizer") - ) - } /** - * A taint step for the `markdown-it` library. + * A taint step for the `marked` library, that converts markdown to HTML. */ - private class MarkdownItStep extends TaintTracking::SharedTaintStep { + private class MarkedStep extends MarkdownStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - exists(API::CallNode call | - call = markdownIt().getMember(["render", "renderInline"]).getACall() + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("marked").getACall() + or + call = DataFlow::moduleImport("marked").getACall() | succ = call and pred = call.getArgument(0) ) } } + + /** + * A taint step for the `markdown-table` library. + */ + private class MarkdownTableStep extends MarkdownStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | call = DataFlow::moduleImport("markdown-table").getACall() | + succ = call and + pred = call.getArgument(0) + ) + } + } + + /** + * A taint step for the `showdown` library. + */ + private class ShowDownStep extends MarkdownStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | + call = + [DataFlow::globalVarRef("showdown"), DataFlow::moduleImport("showdown")] + .getAConstructorInvocation("Converter") + .getAMemberCall(["makeHtml", "makeMd"]) + | + succ = call and + pred = call.getArgument(0) + ) + } + } + + /** + * Classes and predicates for modelling taint steps in `unified` and `remark`. + */ + private module Unified { + /** + * The creation of a parser from `unified`. + * The `remark` module is a shorthand that initializes `unified` with a markdown parser. + */ + DataFlow::CallNode unified() { + result = DataFlow::moduleImport(["unified", "remark"]).getACall() + } + + /** + * A chain of method calls that process an input with `unified`. + */ + class UnifiedChain extends DataFlow::CallNode { + DataFlow::CallNode root; + + UnifiedChain() { + root = unified() and + this = root.getAChainedMethodCall(["process", "processSync"]) + } + + /** + * Gets a plugin that is used in this chain. + */ + DataFlow::Node getAUsedPlugin() { result = root.getAChainedMethodCall("use").getArgument(0) } + + /** + * Gets the input that is processed. + */ + DataFlow::Node getInput() { result = getArgument(0) } + + /** + * Gets the processed output. + */ + DataFlow::Node getOutput() { + this.getCalleeName() = "process" and result = getABoundCallbackParameter(1, 1) + or + this.getCalleeName() = "processSync" and result = this + } + } + + /** + * A taint step for the `unified` library. + */ + class UnifiedStep extends MarkdownStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(UnifiedChain chain | + // sanitizer. Mostly looking for `rehype-sanitize`, but also other plugins with `sanitize` in their name. + not chain.getAUsedPlugin().getALocalSource() = + DataFlow::moduleImport(any(string s | s.matches("%sanitize%"))) + | + pred = chain.getInput() and + succ = chain.getOutput() + ) + } + } + } + + /** + * A taint step for the `snarkdown` library. + */ + private class SnarkdownStep extends MarkdownStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | call = DataFlow::moduleImport("snarkdown").getACall() | + call = succ and + pred = call.getArgument(0) + ) + } + } + + /** + * Classes and predicates for modelling taint steps the `markdown-it` library. + */ + private module MarkdownIt { + /** + * The creation of a parser from `markdown-it`. + */ + private API::Node markdownIt() { + exists(API::InvokeNode call | + call = API::moduleImport("markdown-it").getAnInvocation() + or + call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation() + | + call.getParameter(0).getMember("html").getARhs().mayHaveBooleanValue(true) and + result = call.getReturn() + ) + or + exists(API::CallNode call | + call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and + result = call.getReturn() and + not call.getParameter(0).getAValueReachingRhs() = + DataFlow::moduleImport("markdown-it-sanitizer") + ) + } + + /** + * A taint step for the `markdown-it` library. + */ + private class MarkdownItStep extends MarkdownStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(API::CallNode call | + call = markdownIt().getMember(["render", "renderInline"]).getACall() + | + succ = call and + pred = call.getArgument(0) + ) + } + } + } } From e86a3b5e577fc603c5cf1dd171c191b7e692e373 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 10:21:25 +0200 Subject: [PATCH 199/550] add js/html-constructed-from-input query --- .../CWE-079/UnsafeHtmlConstruction.ql | 22 +++ .../semmle/javascript/frameworks/Markdown.qll | 3 + .../dataflow/UnsafeHtmlConstruction.qll | 32 ++++ .../UnsafeHtmlConstructionCustomizations.qll | 168 ++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql create mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll create mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql new file mode 100644 index 00000000000..464f4951307 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql @@ -0,0 +1,22 @@ +/** + * @name Unsafe HTML constructed from library input + * @description Using externally controlled strings to construct HTML might allow a malicious + * user to perform an cross-site scripting attack. + * @kind path-problem + * @problem.severity error + * @precision high + * @id js/html-constructed-from-input + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import javascript +import DataFlow::PathGraph +import semmle.javascript.security.dataflow.UnsafeHtmlConstruction::UnsafeHtmlConstruction + +from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode +where cfg.hasFlowPath(source, sink) and sink.getNode() = sinkNode +select sinkNode, source, sink, "$@ based on $@ might later cause $@.", sinkNode, + sinkNode.describe(), source.getNode(), "library input", sinkNode.getSink(), + sinkNode.getVulnerabilityKind().toLowerCase() diff --git a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll index 788e28e2e9e..c98243e4fbb 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll @@ -13,6 +13,9 @@ module Markdown { * A taint-step that parses a markdown document, but preserves taint.import */ class MarkdownStep extends Unit { + /** + * Holds if there is a taint-step from `pred` to `succ` for a taint-preserving markdown parser. + */ abstract predicate step(DataFlow::Node pred, DataFlow::Node succ); } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll new file mode 100644 index 00000000000..3e2bf53bde9 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -0,0 +1,32 @@ +/** + * Provides a taint-tracking configuration for reasoning about + * unsafe HTML constructed from library input vulnerabilities. + */ + +import javascript + +/** + * Classes and predicates for the unsafe HTML constructed from library input query. + */ +module UnsafeHtmlConstruction { + private import semmle.javascript.security.dataflow.DomBasedXssCustomizations::DomBasedXss as DomBasedXss + private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQueryPlugin + import UnsafeHtmlConstructionCustomizations::UnsafeHtmlConstruction + + /** + * A taint-tracking configuration for reasoning about unsafe HTML constructed from library input vulnerabilities. + */ + class Configration extends TaintTracking::Configuration { + Configration() { this = "UnsafeHtmlConstruction" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) } + + override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { + DomBasedXss::isOptionallySanitizedEdge(pred, succ) + } + } +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll new file mode 100644 index 00000000000..b72df1b7421 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -0,0 +1,168 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * unsafe HTML constructed from library input, as well as extension points + * for adding your own. + */ + +import javascript + +/** + * Module containing sources, sinks, and sanitizers for unsafe HTML constructed from library input. + */ +module UnsafeHtmlConstruction { + private import semmle.javascript.security.dataflow.DomBasedXssCustomizations::DomBasedXss as DomBasedXss + private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQueryPlugin + private import semmle.javascript.PackageExports as Exports + + /** + * A source for unsafe HTML constructed from library input. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A parameter of an exported function, seen as a source for usnafe HTML constructed from input. + */ + class ExternalInputSource extends Source, DataFlow::ParameterNode { + ExternalInputSource() { + this = Exports::getALibraryInputParameter() and + not this = JQuery::dollarSource() + } + } + + /** + * A sink for unsafe HTML constructed from library input. + * This sink somehow transforms its input into a value that can cause XSS if it ends up in a XSS sink. + */ + abstract class Sink extends DataFlow::Node { + /** + * Gets the kind of vulnerability to report in the alert message. + * + * Defaults to `Cross-site scripting`, but may be overriden for sinks + * that do not allow script injection, but injection of other undesirable HTML elements. + */ + abstract string getVulnerabilityKind(); + + /** + * Gets the XSS sink that this transformed input ends up in. + */ + abstract DataFlow::Node getSink(); + + /** + * Gets a string describing the transformation that this sink represents. + */ + abstract string describe(); + } + + /** + * A sink for `js/html-constructed-from-input` that constructs some HTML where + * that HTML is later used in `xssSink`. + */ + abstract class XssSink extends Sink { + DomBasedXss::Sink xssSink; + + final override string getVulnerabilityKind() { result = xssSink.getVulnerabilityKind() } + + final override DomBasedXss::Sink getSink() { result = xssSink } + } + + /** + * Gets a dataflow node that flows to `sink` tracked by `t`. + */ + private DataFlow::Node isUsedInXssSink(DataFlow::TypeBackTracker t, DomBasedXss::Sink sink) { + t.start() and + result = sink + or + exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, isUsedInXssSink(t2, sink))) + or + exists(DataFlow::TypeBackTracker t2 | + t.continue() = t2 and + domBasedTaintStep(result, isUsedInXssSink(t2, sink)) + ) + } + + /** + * Gets a dataflow node that flows to `sink`. + */ + DataFlow::Node isUsedInXssSink(DomBasedXss::Sink sink) { + result = isUsedInXssSink(DataFlow::TypeBackTracker::end(), sink) + } + + /** + * Holds if there is a taint step from `pred` to `succ` for DOM strings/nodes. + * These steps are mostly relevant for DOM nodes that are created by an XML parser. + */ + predicate domBasedTaintStep(DataFlow::Node pred, DataFlow::SourceNode succ) { + // node.appendChild(newChild) and similar + exists(DataFlow::MethodCallNode call | + call.getMethodName() = ["insertBefore", "replaceChild", "appendChild"] + | + pred = call.getArgument(0) and + succ = [call.getReceiver().getALocalSource(), call] + ) + or + // element.{prepend,append}(node) and similar + exists(DataFlow::MethodCallNode call | + call.getMethodName() = ["prepend", "append", "replaceWith", "replaceChildren"] + | + pred = call.getAnArgument() and + succ = call.getReceiver().getALocalSource() + ) + or + // node.insertAdjacentElement("location", newChild) + exists(DataFlow::MethodCallNode call | call.getMethodName() = "insertAdjacentElement" | + pred = call.getArgument(1) and + succ = call.getReceiver().getALocalSource() + ) + or + // clone = node.cloneNode() + exists(DataFlow::MethodCallNode cloneNode | cloneNode.getMethodName() = "cloneNode" | + pred = cloneNode.getReceiver() and + succ = cloneNode + ) + or + // var succ = pred.documentElement; + // documentElement is the root element of the document, and childNodes is the list of all children + exists(DataFlow::PropRead read | read.getPropertyName() = ["documentElement", "childNodes"] | + pred = read.getBase() and + succ = read + ) + } + + /** + * A string-concatenation of HTML, where the result is used as an XSS sink. + */ + class HTMLConcatenationSink extends XssSink, StringOps::HtmlConcatenationLeaf { + HTMLConcatenationSink() { isUsedInXssSink(xssSink) = this.getRoot() } + + override string describe() { result = "HTML construction" } + } + + /** + * A string parsed as XML, which is later used in an XSS sink. + */ + class XMLParsedSink extends XssSink { + XML::ParserInvocation parser; + + XMLParsedSink() { + this.asExpr() = parser.getSourceArgument() and + isUsedInXssSink(xssSink) = parser.getAResult() + } + + override string describe() { result = "XML parsing" } + } + + /** + * A string rendered as markdown, where the rendering preserves HTML. + */ + class MarkdownSink extends XssSink { + MarkdownSink() { + exists(DataFlow::Node pred, DataFlow::Node succ, Markdown::MarkdownStep step | + step.step(pred, succ) and + this = pred and + succ = isUsedInXssSink(xssSink) + ) + } + + override string describe() { result = "Markdown rendering" } + } +} From 6e754c70aa7f5828799aa3291a84a30bceef1c89 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 10:21:53 +0200 Subject: [PATCH 200/550] add test for js/html-constructed-from-input --- ...ConsistencyUnsafeHtmlConstruction.expected | 0 .../ConsistencyUnsafeHtmlConstruction.ql | 3 + .../UnsafeHtmlConstruction.expected | 72 +++++++++++++++++++ .../UnsafeHtmlConstruction.qlref | 1 + .../UnsafeHtmlConstruction/jquery-plugin.js | 9 +++ .../CWE-079/UnsafeHtmlConstruction/main.js | 54 ++++++++++++++ .../UnsafeHtmlConstruction/package.json | 4 ++ .../UnsafeHtmlConstruction/tsconfig.json | 1 + .../CWE-079/UnsafeHtmlConstruction/typed.ts | 20 ++++++ 9 files changed, 164 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.ql create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/jquery-plugin.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/package.json create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/tsconfig.json create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.ql b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.ql new file mode 100644 index 00000000000..823644730b2 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/ConsistencyUnsafeHtmlConstruction.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.UnsafeHtmlConstruction as UnsafeHtmlConstruction diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected new file mode 100644 index 00000000000..0a8c8013cbc --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -0,0 +1,72 @@ +nodes +| main.js:1:55:1:55 | s | +| main.js:1:55:1:55 | s | +| main.js:2:29:2:29 | s | +| main.js:2:29:2:29 | s | +| main.js:6:49:6:49 | s | +| main.js:6:49:6:49 | s | +| main.js:7:49:7:49 | s | +| main.js:7:49:7:49 | s | +| main.js:11:60:11:60 | s | +| main.js:11:60:11:60 | s | +| main.js:12:49:12:49 | s | +| main.js:12:49:12:49 | s | +| main.js:21:47:21:47 | s | +| main.js:21:47:21:47 | s | +| main.js:22:34:22:34 | s | +| main.js:22:34:22:34 | s | +| typed.ts:1:39:1:39 | s | +| typed.ts:1:39:1:39 | s | +| typed.ts:2:29:2:29 | s | +| typed.ts:2:29:2:29 | s | +| typed.ts:6:43:6:43 | s | +| typed.ts:6:43:6:43 | s | +| typed.ts:8:40:8:40 | s | +| typed.ts:8:40:8:40 | s | +| typed.ts:11:20:11:20 | s | +| typed.ts:11:20:11:20 | s | +| typed.ts:12:12:12:12 | s | +| typed.ts:16:11:16:21 | s | +| typed.ts:16:15:16:21 | id("x") | +| typed.ts:17:29:17:29 | s | +| typed.ts:17:29:17:29 | s | +edges +| main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | +| main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | +| main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | +| main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | +| main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | +| main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | +| main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | +| main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | +| main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | +| main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | +| main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | +| main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | +| main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | +| main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | +| main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | +| main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | +| typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | +| typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | +| typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | +| typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | +| typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | +| typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | +| typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | +| typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | +| typed.ts:11:20:11:20 | s | typed.ts:12:12:12:12 | s | +| typed.ts:11:20:11:20 | s | typed.ts:12:12:12:12 | s | +| typed.ts:12:12:12:12 | s | typed.ts:16:15:16:21 | id("x") | +| typed.ts:16:11:16:21 | s | typed.ts:17:29:17:29 | s | +| typed.ts:16:11:16:21 | s | typed.ts:17:29:17:29 | s | +| typed.ts:16:15:16:21 | id("x") | typed.ts:16:11:16:21 | s | +#select +| main.js:2:29:2:29 | s | main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | $@ based on $@ might later cause $@. | main.js:2:29:2:29 | s | HTML construction | main.js:1:55:1:55 | s | library input | main.js:3:49:3:52 | html | cross-site scripting | +| main.js:7:49:7:49 | s | main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | $@ based on $@ might later cause $@. | main.js:7:49:7:49 | s | XML parsing | main.js:6:49:6:49 | s | library input | main.js:8:48:8:66 | doc.documentElement | cross-site scripting | +| main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:16:21:16:35 | xml.cloneNode() | cross-site scripting | +| main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:17:48:17:50 | tmp | cross-site scripting | +| main.js:22:34:22:34 | s | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | $@ based on $@ might later cause $@. | main.js:22:34:22:34 | s | Markdown rendering | main.js:21:47:21:47 | s | library input | main.js:23:53:23:56 | html | cross-site scripting | +| typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | +| typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | +| typed.ts:17:29:17:29 | s | typed.ts:11:20:11:20 | s | typed.ts:17:29:17:29 | s | $@ based on $@ might later cause $@. | typed.ts:17:29:17:29 | s | HTML construction | typed.ts:11:20:11:20 | s | library input | typed.ts:18:31:18:34 | html | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.qlref b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.qlref new file mode 100644 index 00000000000..0fbe0ed0ba1 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.qlref @@ -0,0 +1 @@ +Security/CWE-079/UnsafeHtmlConstruction.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/jquery-plugin.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/jquery-plugin.js new file mode 100644 index 00000000000..07b25b558f9 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/jquery-plugin.js @@ -0,0 +1,9 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery', 'jquery-ui'], factory); + } else { + factory(jQuery); + } +}(function ($) { + $("" + $.trim("foo") + ""); // OK +})); diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js new file mode 100644 index 00000000000..6564cd2dfe7 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -0,0 +1,54 @@ +module.exports.xssThroughHTMLConstruction = function (s) { + const html = "" + s + "";// NOT OK + document.querySelector("#html").innerHTML = html; +} + +module.exports.xssThroughXMLParsing = function (s) { + const doc = new DOMParser().parseFromString(s, "text/xml"); // NOT OK + document.querySelector("#xml").appendChild(doc.documentElement); +} + +module.exports.xssThroughMoreComplexXMLParsing = function (s) { + const doc = new DOMParser().parseFromString(s, "text/xml"); // NOT OK + const xml = doc.documentElement; + + const tmp = document.createElement('span'); + tmp.appendChild(xml.cloneNode()); + document.querySelector("#xml").appendChild(tmp); +} + +const markdown = require('markdown-it')({html: true}); +module.exports.xssThroughMarkdown = function (s) { + const html = markdown.render(s); // NOT OK + document.querySelector("#markdown").innerHTML = html; +} + +const striptags = require('striptags'); +module.exports.sanitizedHTML = function (s) { + const html = striptags("" + s + ""); // OK + document.querySelector("#sanitized").innerHTML = html; +} + +module.exports.ts = require("./typed"); + +module.exports.jquery = require("./jquery-plugin"); + +module.exports.plainDOMXMLParsing = function (s) { + const doc = new DOMParser().parseFromString(s, "text/xml"); // OK - is never added to the DOM. +} + +class Foo { + constructor(s) { + this.step = s; + } + + doXss() { + // not called here, but still bad. + document.querySelector("#class").innerHTML = "" + this.step + ""; // NOT OK - but not flagged [INCONSISTENCY] + } + +} + +module.exports.createsClass = function (s) { + return new Foo(s); +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/package.json b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/package.json new file mode 100644 index 00000000000..f4fa73f79d9 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/package.json @@ -0,0 +1,4 @@ +{ + "name": "my-unsafe-library", + "main": "./main.js" +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/tsconfig.json b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/tsconfig.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts new file mode 100644 index 00000000000..73c035c0ad2 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts @@ -0,0 +1,20 @@ +export function basicHtmlConstruction(s: string) { + const html = "" + s + ""; // NOT OK + document.body.innerHTML = html; +} + +export function insertIntoCreatedDocument(s: string) { + const newDoc = document.implementation.createHTMLDocument(""); + newDoc.body.innerHTML = "" + s + ""; // OK - inserted into document disconnected from the main DOM. [INCONSISTENCY] +} + +export function id(s: string) { + return s; +} + +export function notVulnerable() { + const s = id("x"); + const html = "" + s + ""; // OK - but flagged due to step with unmatched call [INCONSISTENCY] + document.body.innerHTML = html; +} + \ No newline at end of file From 23908f9ec2ba6166bf161ba25d2f61e3a1f2373a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 10:23:27 +0200 Subject: [PATCH 201/550] remove flowpaths that has a returns without a matching call --- .../dataflow/UnsafeHtmlConstruction.qll | 10 ++++++++ .../UnsafeHtmlConstructionCustomizations.qll | 24 +++++++++++++++++++ .../UnsafeHtmlConstruction.expected | 15 +++++++++++- .../CWE-079/UnsafeHtmlConstruction/main.js | 2 +- .../CWE-079/UnsafeHtmlConstruction/typed.ts | 2 +- 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll index 3e2bf53bde9..7e0194defd3 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -28,5 +28,15 @@ module UnsafeHtmlConstruction { override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { DomBasedXss::isOptionallySanitizedEdge(pred, succ) } + + // override to require that there is a path without unmatched return steps + override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { + super.hasFlowPath(source, sink) and + requireMatchedReturn(source, sink) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + classFieldStep(pred, succ) + } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index b72df1b7421..845438b6b17 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -165,4 +165,28 @@ module UnsafeHtmlConstruction { override string describe() { result = "Markdown rendering" } } + + /** + * A taint step from the write of a field in a constructor to a read of the same field in an instance method. + */ + predicate classFieldStep(DataFlow::Node pred, DataFlow::Node succ) { + // flow-step from a property written in the constructor to a use in an instance method. + // "simulates" client usage of a class, and regains some flow-steps lost by `requireMatchedReturn` below. + exists(DataFlow::ClassNode clazz, string prop | + DataFlow::thisNode(clazz.getConstructor().getFunction()).getAPropertyWrite(prop).getRhs() = + pred and + DataFlow::thisNode(clazz.getAnInstanceMethod().getFunction()).getAPropertyRead(prop) = succ + ) + } + + /** + * Holds if there is a path without unmatched return steps from `source` to `sink`. + */ + predicate requireMatchedReturn(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { + exists(DataFlow::MidPathNode mid | + source.getASuccessor*() = mid and + sink = mid.getASuccessor() and + mid.getPathSummary().hasReturn() = false + ) + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index 0a8c8013cbc..3f87d690d5b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -15,6 +15,13 @@ nodes | main.js:21:47:21:47 | s | | main.js:22:34:22:34 | s | | main.js:22:34:22:34 | s | +| main.js:46:17:46:17 | s | +| main.js:47:21:47:21 | s | +| main.js:52:65:52:73 | this.step | +| main.js:52:65:52:73 | this.step | +| main.js:57:41:57:41 | s | +| main.js:57:41:57:41 | s | +| main.js:58:20:58:20 | s | | typed.ts:1:39:1:39 | s | | typed.ts:1:39:1:39 | s | | typed.ts:2:29:2:29 | s | @@ -47,6 +54,12 @@ edges | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | +| main.js:46:17:46:17 | s | main.js:47:21:47:21 | s | +| main.js:47:21:47:21 | s | main.js:52:65:52:73 | this.step | +| main.js:47:21:47:21 | s | main.js:52:65:52:73 | this.step | +| main.js:57:41:57:41 | s | main.js:58:20:58:20 | s | +| main.js:57:41:57:41 | s | main.js:58:20:58:20 | s | +| main.js:58:20:58:20 | s | main.js:46:17:46:17 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | @@ -67,6 +80,6 @@ edges | main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:16:21:16:35 | xml.cloneNode() | cross-site scripting | | main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:17:48:17:50 | tmp | cross-site scripting | | main.js:22:34:22:34 | s | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | $@ based on $@ might later cause $@. | main.js:22:34:22:34 | s | Markdown rendering | main.js:21:47:21:47 | s | library input | main.js:23:53:23:56 | html | cross-site scripting | +| main.js:52:65:52:73 | this.step | main.js:57:41:57:41 | s | main.js:52:65:52:73 | this.step | $@ based on $@ might later cause $@. | main.js:52:65:52:73 | this.step | HTML construction | main.js:57:41:57:41 | s | library input | main.js:52:54:52:85 | " ... /span>" | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | | typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | -| typed.ts:17:29:17:29 | s | typed.ts:11:20:11:20 | s | typed.ts:17:29:17:29 | s | $@ based on $@ might later cause $@. | typed.ts:17:29:17:29 | s | HTML construction | typed.ts:11:20:11:20 | s | library input | typed.ts:18:31:18:34 | html | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js index 6564cd2dfe7..d3620615c08 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -44,7 +44,7 @@ class Foo { doXss() { // not called here, but still bad. - document.querySelector("#class").innerHTML = "" + this.step + ""; // NOT OK - but not flagged [INCONSISTENCY] + document.querySelector("#class").innerHTML = "" + this.step + ""; // NOT OK } } diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts index 73c035c0ad2..0f04e92cdc0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/typed.ts @@ -14,7 +14,7 @@ export function id(s: string) { export function notVulnerable() { const s = id("x"); - const html = "" + s + ""; // OK - but flagged due to step with unmatched call [INCONSISTENCY] + const html = "" + s + ""; // OK document.body.innerHTML = html; } \ No newline at end of file From ee0140e7044ea81b7b08c20e68dbcce54ab5366b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 10:23:44 +0200 Subject: [PATCH 202/550] share code between unsafe-shell and unsafe-html queries --- .../security/dataflow/UnsafeHtmlConstruction.qll | 2 +- .../UnsafeHtmlConstructionCustomizations.qll | 13 ------------- .../dataflow/UnsafeShellCommandConstruction.qll | 15 +++------------ 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll index 7e0194defd3..8d2855dd766 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -36,7 +36,7 @@ module UnsafeHtmlConstruction { } override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - classFieldStep(pred, succ) + DataFlow::localFieldStep(pred, succ) } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 845438b6b17..26da71aa633 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -166,19 +166,6 @@ module UnsafeHtmlConstruction { override string describe() { result = "Markdown rendering" } } - /** - * A taint step from the write of a field in a constructor to a read of the same field in an instance method. - */ - predicate classFieldStep(DataFlow::Node pred, DataFlow::Node succ) { - // flow-step from a property written in the constructor to a use in an instance method. - // "simulates" client usage of a class, and regains some flow-steps lost by `requireMatchedReturn` below. - exists(DataFlow::ClassNode clazz, string prop | - DataFlow::thisNode(clazz.getConstructor().getFunction()).getAPropertyWrite(prop).getRhs() = - pred and - DataFlow::thisNode(clazz.getAnInstanceMethod().getFunction()).getAPropertyRead(prop) = succ - ) - } - /** * Holds if there is a path without unmatched return steps from `source` to `sink`. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll index 90caf59ad33..2c7de7555de 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll @@ -14,6 +14,7 @@ import javascript */ module UnsafeShellCommandConstruction { import UnsafeShellCommandConstructionCustomizations::UnsafeShellCommandConstruction + import UnsafeHtmlConstructionCustomizations /** * A taint-tracking configuration for reasoning about shell command constructed from library input vulnerabilities. @@ -35,21 +36,11 @@ module UnsafeShellCommandConstruction { // override to require that there is a path without unmatched return steps override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { super.hasFlowPath(source, sink) and - exists(DataFlow::MidPathNode mid | - source.getASuccessor*() = mid and - sink = mid.getASuccessor() and - mid.getPathSummary().hasReturn() = false - ) + UnsafeHtmlConstruction::requireMatchedReturn(source, sink) } override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - // flow-step from a property written in the constructor to a use in an instance method. - // "simulates" client usage of a class, and regains some flow-steps lost by `hasFlowPath` above. - exists(DataFlow::ClassNode clz, string name | - pred = - DataFlow::thisNode(clz.getConstructor().getFunction()).getAPropertyWrite(name).getRhs() and - succ = DataFlow::thisNode(clz.getInstanceMethod(_).getFunction()).getAPropertyRead(name) - ) + DataFlow::localFieldStep(pred, succ) } } } From 7ef641e7b256754803b1e0f1531916010181739b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 11:47:52 +0200 Subject: [PATCH 203/550] add qhelp --- .../CWE-079/UnsafeHtmlConstruction.qhelp | 80 +++++++++++++++++++ .../examples/unsafe-html-construction.js | 3 + .../examples/unsafe-html-construction_safe.js | 5 ++ .../unsafe-html-construction_sanitizer.js | 5 ++ 4 files changed, 93 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp create mode 100644 javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction.js create mode 100644 javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_safe.js create mode 100644 javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_sanitizer.js diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp new file mode 100644 index 00000000000..12e030150b6 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp @@ -0,0 +1,80 @@ + + + +

    + Dynamically constructing HTML with inputs from exported functions may + inadvertently leave a client open to XSS attacks. + + Clients using the exported function may use inputs containing unsafe HTML, + and if these inputs end up in the DOM, the client may be vulnerable to + cross-site scripting attacks. +

    + +
    + + +

    + If possible, use safe APIs when inserting HTML into the DOM. + Such as writing to the innerText property instead of innerHTML. +

    + +

    + Alternatively, use a HTML sanitizer to escape/remove unsafe content. +

    + +
    + + +

    + The following example shows a library function that shows a boldface name + by writing to the innerHTML property of an element. +

    + + + +

    + This library function, however, does not escape unsafe HTML, and a client + that calls the function with user-supplied input may be vulnerable to + cross-site scripting attacks. +

    + +

    + To avoid such attacks, a program can use safe APIs such as innerText. +

    + + + +

    + Alternatively, use a HTML sanitizer to remove unsafe content. +

    + + + +
    + +
  • + OWASP: + DOM based + XSS Prevention Cheat Sheet. +
  • +
  • + OWASP: + XSS + (Cross Site Scripting) Prevention Cheat Sheet. +
  • +
  • + OWASP + DOM Based XSS. +
  • +
  • + OWASP + Types of Cross-Site + Scripting. +
  • +
  • + Wikipedia: Cross-site scripting. +
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction.js b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction.js new file mode 100644 index 00000000000..ab4bfee0a25 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction.js @@ -0,0 +1,3 @@ +module.exports = function showBoldName(name) { + document.getElementById('name').innerHTML = "" + name + ""; +} diff --git a/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_safe.js b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_safe.js new file mode 100644 index 00000000000..b2b3af9bcfb --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_safe.js @@ -0,0 +1,5 @@ +module.exports = function showBoldName(name) { + const bold = document.createElement('b'); + bold.innerText = name; + document.getElementById('name').appendChild(bold); +} diff --git a/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_sanitizer.js b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_sanitizer.js new file mode 100644 index 00000000000..9c63e278720 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/unsafe-html-construction_sanitizer.js @@ -0,0 +1,5 @@ + +const striptags = require('striptags'); +module.exports = function showBoldName(name) { + document.getElementById('name').innerHTML = "" + striptags(name) + ""; +} From 5c37e6a435ae1a1f048d087af68daedd0d9f40ab Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 11:56:44 +0200 Subject: [PATCH 204/550] add change note --- javascript/change-notes/2021-04-26-unsafe-html-construction.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 javascript/change-notes/2021-04-26-unsafe-html-construction.md diff --git a/javascript/change-notes/2021-04-26-unsafe-html-construction.md b/javascript/change-notes/2021-04-26-unsafe-html-construction.md new file mode 100644 index 00000000000..8b0f513d760 --- /dev/null +++ b/javascript/change-notes/2021-04-26-unsafe-html-construction.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* A new query, `js/html-constructed-from-input`, has been added to the query suite, + highlighting libraries that may leave clients vulnerable to cross-site-scripting attacks. From 8ba5bddae8d99817107ebfea1f3527b27cdd74f7 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 20:03:14 +0200 Subject: [PATCH 205/550] add jQuery options objects as sources --- .../UnsafeHtmlConstructionCustomizations.qll | 8 +++ .../UnsafeHtmlConstruction.expected | 51 ++++++++++++++----- .../CWE-079/UnsafeHtmlConstruction/main.js | 10 ++++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 26da71aa633..49097c81379 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -13,6 +13,7 @@ module UnsafeHtmlConstruction { private import semmle.javascript.security.dataflow.DomBasedXssCustomizations::DomBasedXss as DomBasedXss private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQueryPlugin private import semmle.javascript.PackageExports as Exports + private import semmle.javascript.security.dataflow.UnsafeJQueryPlugin::UnsafeJQueryPlugin as UnsafeJQueryPlugin /** * A source for unsafe HTML constructed from library input. @@ -29,6 +30,13 @@ module UnsafeHtmlConstruction { } } + /** + * A jQuery plugin options object, seen as a source for unsafe HTML constructed from input. + */ + class JQueryPluginOptionsAsSource extends Source { + JQueryPluginOptionsAsSource() { this instanceof UnsafeJQueryPlugin::JQueryPluginOptions } + } + /** * A sink for unsafe HTML constructed from library input. * This sink somehow transforms its input into a value that can cause XSS if it ends up in a XSS sink. diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index 3f87d690d5b..73e2177b198 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -15,13 +15,24 @@ nodes | main.js:21:47:21:47 | s | | main.js:22:34:22:34 | s | | main.js:22:34:22:34 | s | -| main.js:46:17:46:17 | s | -| main.js:47:21:47:21 | s | -| main.js:52:65:52:73 | this.step | -| main.js:52:65:52:73 | this.step | -| main.js:57:41:57:41 | s | -| main.js:57:41:57:41 | s | -| main.js:58:20:58:20 | s | +| main.js:41:17:41:17 | s | +| main.js:42:21:42:21 | s | +| main.js:47:65:47:73 | this.step | +| main.js:47:65:47:73 | this.step | +| main.js:52:41:52:41 | s | +| main.js:52:41:52:41 | s | +| main.js:53:20:53:20 | s | +| main.js:56:28:56:34 | options | +| main.js:56:28:56:34 | options | +| main.js:57:11:59:5 | defaults | +| main.js:57:22:59:5 | {\\n ... "\\n } | +| main.js:60:11:60:48 | settings | +| main.js:60:22:60:48 | $.exten ... ptions) | +| main.js:60:31:60:38 | defaults | +| main.js:60:41:60:47 | options | +| main.js:62:19:62:26 | settings | +| main.js:62:19:62:31 | settings.name | +| main.js:62:19:62:31 | settings.name | | typed.ts:1:39:1:39 | s | | typed.ts:1:39:1:39 | s | | typed.ts:2:29:2:29 | s | @@ -54,12 +65,23 @@ edges | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | -| main.js:46:17:46:17 | s | main.js:47:21:47:21 | s | -| main.js:47:21:47:21 | s | main.js:52:65:52:73 | this.step | -| main.js:47:21:47:21 | s | main.js:52:65:52:73 | this.step | -| main.js:57:41:57:41 | s | main.js:58:20:58:20 | s | -| main.js:57:41:57:41 | s | main.js:58:20:58:20 | s | -| main.js:58:20:58:20 | s | main.js:46:17:46:17 | s | +| main.js:41:17:41:17 | s | main.js:42:21:42:21 | s | +| main.js:42:21:42:21 | s | main.js:47:65:47:73 | this.step | +| main.js:42:21:42:21 | s | main.js:47:65:47:73 | this.step | +| main.js:52:41:52:41 | s | main.js:53:20:53:20 | s | +| main.js:52:41:52:41 | s | main.js:53:20:53:20 | s | +| main.js:53:20:53:20 | s | main.js:41:17:41:17 | s | +| main.js:56:28:56:34 | options | main.js:60:41:60:47 | options | +| main.js:56:28:56:34 | options | main.js:60:41:60:47 | options | +| main.js:57:11:59:5 | defaults | main.js:60:31:60:38 | defaults | +| main.js:57:22:59:5 | {\\n ... "\\n } | main.js:57:11:59:5 | defaults | +| main.js:60:11:60:48 | settings | main.js:62:19:62:26 | settings | +| main.js:60:22:60:48 | $.exten ... ptions) | main.js:60:11:60:48 | settings | +| main.js:60:31:60:38 | defaults | main.js:60:22:60:48 | $.exten ... ptions) | +| main.js:60:41:60:47 | options | main.js:57:22:59:5 | {\\n ... "\\n } | +| main.js:60:41:60:47 | options | main.js:60:22:60:48 | $.exten ... ptions) | +| main.js:62:19:62:26 | settings | main.js:62:19:62:31 | settings.name | +| main.js:62:19:62:26 | settings | main.js:62:19:62:31 | settings.name | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | @@ -80,6 +102,7 @@ edges | main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:16:21:16:35 | xml.cloneNode() | cross-site scripting | | main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:17:48:17:50 | tmp | cross-site scripting | | main.js:22:34:22:34 | s | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | $@ based on $@ might later cause $@. | main.js:22:34:22:34 | s | Markdown rendering | main.js:21:47:21:47 | s | library input | main.js:23:53:23:56 | html | cross-site scripting | -| main.js:52:65:52:73 | this.step | main.js:57:41:57:41 | s | main.js:52:65:52:73 | this.step | $@ based on $@ might later cause $@. | main.js:52:65:52:73 | this.step | HTML construction | main.js:57:41:57:41 | s | library input | main.js:52:54:52:85 | " ... /span>" | cross-site scripting | +| main.js:47:65:47:73 | this.step | main.js:52:41:52:41 | s | main.js:47:65:47:73 | this.step | $@ based on $@ might later cause $@. | main.js:47:65:47:73 | this.step | HTML construction | main.js:52:41:52:41 | s | library input | main.js:47:54:47:85 | " ... /span>" | cross-site scripting | +| main.js:62:19:62:31 | settings.name | main.js:56:28:56:34 | options | main.js:62:19:62:31 | settings.name | $@ based on $@ might later cause $@. | main.js:62:19:62:31 | settings.name | HTML construction | main.js:56:28:56:34 | options | library input | main.js:62:11:62:40 | "" + ... "" | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | | typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js index d3620615c08..d0a117c2341 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -52,3 +52,13 @@ class Foo { module.exports.createsClass = function (s) { return new Foo(s); } + +$.fn.xssPlugin = function (options) { + const defaults = { + name: "name" + }; + const settings = $.extend(defaults, options); + return this.each(function () { + $("" + settings.name + "").appendTo(this); // NOT OK + }); +} \ No newline at end of file From 3815797dda55766cc497caa08d789e75c8134f70 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 26 Apr 2021 22:39:59 +0200 Subject: [PATCH 206/550] add sanitizers from DOM and jQuery queries --- .../security/dataflow/UnsafeHtmlConstruction.qll | 8 +++++++- .../UnsafeHtmlConstructionCustomizations.qll | 1 - .../UnsafeHtmlConstruction.expected | 9 +++++++++ .../CWE-079/UnsafeHtmlConstruction/main.js | 15 ++++++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll index 8d2855dd766..699d9d3e510 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -23,7 +23,13 @@ module UnsafeHtmlConstruction { override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } - override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) } + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) + or + node instanceof DomBasedXss::Sanitizer + or + node instanceof UnsafeJQueryPlugin::Sanitizer + } override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { DomBasedXss::isOptionallySanitizedEdge(pred, succ) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 49097c81379..a99b5e7986d 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -13,7 +13,6 @@ module UnsafeHtmlConstruction { private import semmle.javascript.security.dataflow.DomBasedXssCustomizations::DomBasedXss as DomBasedXss private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQueryPlugin private import semmle.javascript.PackageExports as Exports - private import semmle.javascript.security.dataflow.UnsafeJQueryPlugin::UnsafeJQueryPlugin as UnsafeJQueryPlugin /** * A source for unsafe HTML constructed from library input. diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index 73e2177b198..7994fbad934 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -33,6 +33,10 @@ nodes | main.js:62:19:62:26 | settings | | main.js:62:19:62:31 | settings.name | | main.js:62:19:62:31 | settings.name | +| main.js:66:35:66:41 | attrVal | +| main.js:66:35:66:41 | attrVal | +| main.js:67:63:67:69 | attrVal | +| main.js:67:63:67:69 | attrVal | | typed.ts:1:39:1:39 | s | | typed.ts:1:39:1:39 | s | | typed.ts:2:29:2:29 | s | @@ -82,6 +86,10 @@ edges | main.js:60:41:60:47 | options | main.js:60:22:60:48 | $.exten ... ptions) | | main.js:62:19:62:26 | settings | main.js:62:19:62:31 | settings.name | | main.js:62:19:62:26 | settings | main.js:62:19:62:31 | settings.name | +| main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | +| main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | +| main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | +| main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | @@ -104,5 +112,6 @@ edges | main.js:22:34:22:34 | s | main.js:21:47:21:47 | s | main.js:22:34:22:34 | s | $@ based on $@ might later cause $@. | main.js:22:34:22:34 | s | Markdown rendering | main.js:21:47:21:47 | s | library input | main.js:23:53:23:56 | html | cross-site scripting | | main.js:47:65:47:73 | this.step | main.js:52:41:52:41 | s | main.js:47:65:47:73 | this.step | $@ based on $@ might later cause $@. | main.js:47:65:47:73 | this.step | HTML construction | main.js:52:41:52:41 | s | library input | main.js:47:54:47:85 | " ... /span>" | cross-site scripting | | main.js:62:19:62:31 | settings.name | main.js:56:28:56:34 | options | main.js:62:19:62:31 | settings.name | $@ based on $@ might later cause $@. | main.js:62:19:62:31 | settings.name | HTML construction | main.js:56:28:56:34 | options | library input | main.js:62:11:62:40 | "" + ... "" | cross-site scripting | +| main.js:67:63:67:69 | attrVal | main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | $@ based on $@ might later cause $@. | main.js:67:63:67:69 | attrVal | HTML construction | main.js:66:35:66:41 | attrVal | library input | main.js:67:47:67:78 | "" | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | | typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js index d0a117c2341..1097e126feb 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -61,4 +61,17 @@ $.fn.xssPlugin = function (options) { return this.each(function () { $("" + settings.name + "").appendTo(this); // NOT OK }); -} \ No newline at end of file +} + +module.exports.guards = function (attrVal) { + document.querySelector("#id").innerHTML = "\"""; // NOT OK + document.querySelector("#id").innerHTML = "\"""; // OK + if (attrVal.indexOf("\"") === -1 && attrVal.indexOf("'") === -1) { + document.querySelector("#id").innerHTML = "\"""; // OK + } +} + +module.exports.intentionalTemplate = function (obj) { + const html = "" + obj.spanTemplate + ""; // OK + document.querySelector("#template").innerHTML = html; +} From b1a63949594372616573d59206cfc47d58e95704 Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Thu, 6 May 2021 12:36:48 +0300 Subject: [PATCH 207/550] C++: SqlPqxxTainted.ql. Change @id in query metadata --- .../experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql index 9d27674409a..9048c8fdab1 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql @@ -6,7 +6,7 @@ * @kind path-problem * @problem.severity error * @precision high - * @id cpp/sql-injection + * @id cpp/sql-injection-via-pqxx * @tags security * external/cwe/cwe-089 */ @@ -75,7 +75,7 @@ Expr getPqxxSqlArgument() { // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior // and return pointer to a connection/transation object e.getType().refersTo(t) and - // transation exec and connection prepare variations + // transaction exec and connection prepare variations ( pqxxTransationClassNames(t.getName(), _) and pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex) @@ -113,10 +113,10 @@ predicate isEscapedPqxxArgument(Expr argExpr) { // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior // and return pointer to a connection/transation object e.getType().refersTo(t) and - // transation and connection escape functions + // transaction and connection escape functions (pqxxTransationClassNames(t.getName(), _) or pqxxConnectionClassNames(t.getName(), _)) and pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and - // eval is escaped + // is escaped arg == argExpr argExpr = fc.getArgument(argIndex) ) } From 58f304880899e1f84195aaf346544b6a1e0f1add Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 13:15:34 +0200 Subject: [PATCH 208/550] C++: Add more testcases. --- ...AndHandlingMemoryAllocationErrors.expected | 7 ++ .../CWE/CWE-570/semmle/tests/test.cpp | 71 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected index 19886efd28f..04482cb59dc 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected @@ -7,3 +7,10 @@ | test.cpp:92:5:92:31 | call to operator new[] | memory allocation error check is incorrect or missing | | test.cpp:93:15:93:41 | call to operator new[] | memory allocation error check is incorrect or missing | | test.cpp:96:10:96:36 | call to operator new[] | memory allocation error check is incorrect or missing | +| test.cpp:151:9:151:24 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:157:9:157:28 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:182:15:182:35 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:187:15:187:35 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:192:15:192:35 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:199:15:199:35 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:204:15:204:35 | call to operator new | memory allocation error check is incorrect or missing | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 06c22778812..54755ecf51b 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -135,3 +135,74 @@ void good_new_handles_nullptr() { if (new (std::nothrow) int[100] == nullptr) return; // GOOD } + +void* operator new(std::size_t count, void*) noexcept; +void* operator new[](std::size_t count, void*) noexcept; + +struct Foo { + Foo() noexcept; + Foo(int); + + operator bool(); +}; + +void bad_placement_new_with_exception_handling() { + char buffer[1024]; + try { new (buffer) Foo; } // BAD + catch (...) { } +} + +void good_placement_new_with_exception_handling() { + char buffer[1024]; + try { new (buffer) Foo(42); } // GOOD: Foo constructor might throw [FALSE POSITIVE] + catch (...) { } +} + +int rand(); + +void may_throw() { + if(rand()) { + throw "bad luck exception!"; + } +} + +void unknown_code_that_may_throw(int*); +void unknown_code_that_will_not_throw(int*) noexcept; + +void calls_throwing_code(int* p) { + if(rand()) unknown_code_that_may_throw(p); +} + +void calls_non_throwing(int* p) { + if (rand()) unknown_code_that_will_not_throw(p); +} + +void good_new_with_throwing_call() { + try { + int* p1 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + may_throw(); + } catch(...) { } + + try { + int* p2 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + Foo f(10); + } catch(...) { } + + try { + int* p3 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + calls_throwing_code(p3); + } catch(...) { } +} + +void bad_new_with_nonthrowing_call() { + try { + int* p1 = new(std::nothrow) int; // BAD + calls_non_throwing(p1); + } catch(...) { } + + try { + int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw [FALSE POSITIVE] + Foo f(12); + if(f) { } + } catch(...) { } +} \ No newline at end of file From 56d734239807e22a0eacad44bba9ea7cfd27562e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 13:29:20 +0200 Subject: [PATCH 209/550] C++: Improve the cpp/detect-and-handle-memory-allocation-errors query. --- ...ectingAndHandlingMemoryAllocationErrors.ql | 232 +++++++++++++----- 1 file changed, 168 insertions(+), 64 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql index 8c7b8e5d067..58221e07f5c 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql @@ -11,76 +11,180 @@ */ import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.controlflow.Guards /** - * Lookup if condition compare with 0 + * A C++ `delete` or `delete[]` expression. */ -class IfCompareWithZero extends IfStmt { - IfCompareWithZero() { - this.getCondition().(EQExpr).getAChild().getValue() = "0" - or - this.getCondition().(NEExpr).getAChild().getValue() = "0" and - this.hasElse() - or - this.getCondition().(NEExpr).getAChild().getValue() = "0" and - this.getThen().getAChild*() instanceof ReturnStmt +class DeleteOrDeleteArrayExpr extends Expr { + DeleteOrDeleteArrayExpr() { this instanceof DeleteExpr or this instanceof DeleteArrayExpr } + + DeallocationFunction getDeallocator() { + result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()] } } -/** - * lookup for calls to `operator new`, with incorrect error handling. - */ -class WrongCheckErrorOperatorNew extends FunctionCall { - Expr exp; - - WrongCheckErrorOperatorNew() { - this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and - ( - this.getTarget().hasGlobalOrStdName("operator new") - or - this.getTarget().hasGlobalOrStdName("operator new[]") - ) - } - - /** - * Holds if handler `try ... catch` exists. - */ - predicate isExistsTryCatchBlock() { - exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*()) - } - - /** - * Holds if results call `operator new` check in `operator if`. - */ - predicate isExistsIfCondition() { - exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it | - // call `operator new` directly from the condition of `operator if`. - this = ifc.getCondition().getAChild*() - or - // check results call `operator new` with variable appropriation - postDominates(ifc, this) and - aex.getAChild() = exp and - ifc.getCondition().getAChild().(VariableAccess).getTarget() = - aex.getLValue().(VariableAccess).getTarget() - or - // check results call `operator new` with declaration variable - postDominates(ifc, this) and - exp = it.getExpr() and - it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget() - ) - } - - /** - * Holds if `(std::nothrow)` or `(std::noexcept)` exists in call `operator new`. - */ - predicate isExistsNothrow() { getTarget().isNoExcept() or getTarget().isNoThrow() } +/** Gets the `Constructor` invoked when `newExpr` allocates memory. */ +Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) { + result.getACallToThisFunction().getLocation() = newExpr.getLocation() } -from WrongCheckErrorOperatorNew op -where - // use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if` - op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock() +/** Gets the `Destructor` invoked when `deleteExpr` deallocates memory. */ +Destructor getDestructorForDeallocation(DeleteOrDeleteArrayExpr deleteExpr) { + result.getACallToThisFunction().getLocation() = deleteExpr.getLocation() +} + +/** Holds if the evaluation of `newExpr` may throw an exception. */ +predicate newMayThrow(NewOrNewArrayExpr newExpr) { + functionMayThrow(newExpr.getAllocator()) or + functionMayThrow(getConstructorForAllocation(newExpr)) +} + +/** Holds if the evaluation of `deleteExpr` may throw an exception. */ +predicate deleteMayThrow(DeleteOrDeleteArrayExpr deleteExpr) { + functionMayThrow(deleteExpr.getDeallocator()) or + functionMayThrow(getDestructorForDeallocation(deleteExpr)) +} + +/** + * Holds if the function may throw an exception when called. That is, if the body of the function looks + * like it might throw an exception, and the function does not have a `noexcept` or `throw()` specifier. + */ +predicate functionMayThrow(Function f) { + (not exists(f.getBlock()) or stmtMayThrow(f.getBlock())) and + not f.isNoExcept() and + not f.isNoThrow() +} + +/** Holds if the evaluation of `stmt` may throw an exception. */ +predicate stmtMayThrow(Stmt stmt) { + stmtMayThrow(stmt.(BlockStmt).getAStmt()) or - // use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block - not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition() -select op, "memory allocation error check is incorrect or missing" + convertedExprMayThrow(stmt.(ExprStmt).getExpr()) + or + exists(IfStmt ifStmt | ifStmt = stmt | + convertedExprMayThrow(ifStmt.getCondition()) or + stmtMayThrow([ifStmt.getThen(), ifStmt.getElse()]) + ) + or + exists(Loop loop | loop = stmt | + convertedExprMayThrow(loop.getCondition()) or + stmtMayThrow(loop.getStmt()) + ) +} + +/** Holds if the evaluation of `e` (including conversions) may throw an exception. */ +predicate convertedExprMayThrow(Expr e) { exprMayThrow(e.getFullyConverted()) } + +/** Holds if the evaluation of `e` may throw an exception. */ +predicate exprMayThrow(Expr e) { + e instanceof DynamicCast + or + e instanceof TypeidOperator + or + e instanceof ThrowExpr + or + newMayThrow(e) + or + deleteMayThrow(e) + or + convertedExprMayThrow(e.(UnaryOperation).getOperand()) + or + exists(BinaryOperation binOp | binOp = e | + convertedExprMayThrow([binOp.getLeftOperand(), binOp.getRightOperand()]) + ) + or + exists(CommaExpr comma | comma = e | + convertedExprMayThrow([comma.getLeftOperand(), comma.getRightOperand()]) + ) + or + exists(StmtExpr stmtExpr | stmtExpr = e | + convertedExprMayThrow(stmtExpr.getResultExpr()) or + stmtMayThrow(stmtExpr.getStmt()) + ) + or + convertedExprMayThrow(e.(Conversion).getExpr()) + or + exists(FunctionCall fc | fc = e | + not exists(fc.getTarget()) or + functionMayThrow(fc.getTarget()) or + convertedExprMayThrow(fc.getAnArgument()) + ) +} + +/** An allocator that will not throw an exception. */ +class NoThrowAllocator extends Function { + NoThrowAllocator() { + exists(NewOrNewArrayExpr newExpr | + newExpr.getAllocator() = this and + not functionMayThrow(this) + ) + } +} + +/** An allocator that might throw an exception. */ +class ThrowingAllocator extends Function { + ThrowingAllocator() { not this instanceof NoThrowAllocator } +} + +/** The `std::bad_alloc` exception and its `bsl` variant. */ +class BadAllocType extends Class { + BadAllocType() { this.hasGlobalOrStdOrBslName("bad_alloc") } +} + +/** + * A catch block that catches a `std::bad_alloc` (or any of its subclasses), or a catch + * block that catches every exception (i.e., `catch(...)`). + */ +class BadAllocCatchBlock extends CatchBlock { + BadAllocCatchBlock() { + this.getParameter().getUnspecifiedType() = any(BadAllocType badAlloc).getADerivedClass*() + or + not exists(this.getParameter()) + } +} + +/** + * Holds if `newExpr` will not throw an exception, but is embedded in a `try` statement + * with a catch block `catchBlock` that catches an `std::bad_alloc` exception. + */ +predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) { + exists(TryStmt try | + forall(Expr cand | cand.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() | + not convertedExprMayThrow(cand) + ) and + try.getACatchClause() = catchBlock and + newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() and + not newMayThrow(newExpr) + ) +} + +/** + * Holds if `newExpr` is handles allocation failures by throwing an exception, yet + * the guard condition `guard` compares the result of `newExpr` to a null value. + */ +predicate nullCheckInThrowingNew(NewOrNewArrayExpr newExpr, GuardCondition guard) { + newExpr.getAllocator() instanceof ThrowingAllocator and + ( + // Handles null comparisons. + guard.ensuresEq(globalValueNumber(newExpr).getAnExpr(), any(NullValue null), _, _, _) + or + // Handles `if(ptr)` and `if(!ptr)` cases. + guard = globalValueNumber(newExpr).getAnExpr() + ) +} + +from NewOrNewArrayExpr newExpr, Element element, string msg, string elementString +where + not newExpr.isFromUninstantiatedTemplate(_) and + ( + noThrowInTryBlock(newExpr, element) and + msg = "This allocation cannot throw. $@ is unnecessary." and + elementString = "This catch block" + or + nullCheckInThrowingNew(newExpr, element) and + msg = "This allocation cannot return null. $@ is unnecessary." and + elementString = "This check" + ) +select newExpr, msg, element, elementString From d3576b9c92027b0d8371f4ed329adea2bb93159f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 13:29:28 +0200 Subject: [PATCH 210/550] C++: Accept test changes. --- ...AndHandlingMemoryAllocationErrors.expected | 30 +++++++++---------- .../CWE/CWE-570/semmle/tests/test.cpp | 22 +++++++------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected index 04482cb59dc..edd08b9802d 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected @@ -1,16 +1,14 @@ -| test.cpp:29:13:29:24 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:37:13:37:24 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:41:13:41:24 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:49:8:49:19 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:58:8:58:19 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:63:8:63:19 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:92:5:92:31 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:93:15:93:41 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:96:10:96:36 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:151:9:151:24 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:157:9:157:28 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:182:15:182:35 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:187:15:187:35 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:192:15:192:35 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:199:15:199:35 | call to operator new | memory allocation error check is incorrect or missing | -| test.cpp:204:15:204:35 | call to operator new | memory allocation error check is incorrect or missing | +| test.cpp:21:9:21:15 | new | This allocation cannot return null. $@ is unnecessary. | test.cpp:21:9:21:15 | new | This check | +| test.cpp:29:13:29:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:30:7:30:13 | ... == ... | This check | +| test.cpp:33:13:33:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:34:8:34:9 | p2 | This check | +| test.cpp:37:13:37:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:38:7:38:16 | ... == ... | This check | +| test.cpp:41:13:41:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:42:7:42:19 | ... == ... | This check | +| test.cpp:45:13:45:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:46:7:46:8 | p5 | This check | +| test.cpp:49:8:49:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:50:7:50:13 | ... == ... | This check | +| test.cpp:53:8:53:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:54:8:54:9 | p7 | This check | +| test.cpp:58:8:58:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:59:7:59:16 | ... == ... | This check | +| test.cpp:63:8:63:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:64:7:64:19 | ... != ... | This check | +| test.cpp:69:9:69:20 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:70:7:70:14 | ... != ... | This check | +| test.cpp:75:11:75:22 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:76:13:76:15 | p11 | This check | +| test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | +| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 54755ecf51b..8d27c4b727d 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -18,7 +18,7 @@ void *operator new(std::size_t, const std::nothrow_t &) noexcept; void *operator new[](std::size_t, const std::nothrow_t &) noexcept; void bad_new_in_condition() { - if (!(new int)) { // BAD [NOT DETECTED] + if (!(new int)) { // BAD return; } } @@ -30,7 +30,7 @@ void bad_new_missing_exception_handling() { if (p1 == 0) return; - int *p2 = new int[100]; // BAD [NOT DETECTED] + int *p2 = new int[100]; // BAD if (!p2) return; @@ -42,7 +42,7 @@ void bad_new_missing_exception_handling() { if (p4 == nullptr) return; - int *p5 = new int[100]; // BAD [NOT DETECTED] + int *p5 = new int[100]; // BAD if (p5) {} else return; int *p6; @@ -50,7 +50,7 @@ void bad_new_missing_exception_handling() { if (p6 == 0) return; int *p7; - p7 = new int[100]; // BAD [NOT DETECTED] + p7 = new int[100]; // BAD if (!p7) return; @@ -66,13 +66,13 @@ void bad_new_missing_exception_handling() { return; int *p10; - p10 = new int[100]; // BAD [NOT DETECTED] + p10 = new int[100]; // BAD if (p10 != 0) { } int *p11; do { - p11 = new int[100]; // BAD [NOT DETECTED] + p11 = new int[100]; // BAD } while (!p11); int* p12 = new int[100]; @@ -154,7 +154,7 @@ void bad_placement_new_with_exception_handling() { void good_placement_new_with_exception_handling() { char buffer[1024]; - try { new (buffer) Foo(42); } // GOOD: Foo constructor might throw [FALSE POSITIVE] + try { new (buffer) Foo(42); } // GOOD: Foo constructor might throw catch (...) { } } @@ -179,17 +179,17 @@ void calls_non_throwing(int* p) { void good_new_with_throwing_call() { try { - int* p1 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + int* p1 = new(std::nothrow) int; // GOOD may_throw(); } catch(...) { } try { - int* p2 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + int* p2 = new(std::nothrow) int; // GOOD Foo f(10); } catch(...) { } try { - int* p3 = new(std::nothrow) int; // GOOD [FALSE POSITIVE] + int* p3 = new(std::nothrow) int; // GOOD calls_throwing_code(p3); } catch(...) { } } @@ -201,7 +201,7 @@ void bad_new_with_nonthrowing_call() { } catch(...) { } try { - int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw [FALSE POSITIVE] + int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw Foo f(12); if(f) { } } catch(...) { } From 420215931c0546e882e362c89cf03588647424bc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 13:35:08 +0200 Subject: [PATCH 211/550] C++: Rename query. --- ...ocationErrors.cpp => IncorrectAllocationErrorHandling.cpp} | 0 ...ionErrors.qhelp => IncorrectAllocationErrorHandling.qhelp} | 2 +- ...llocationErrors.ql => IncorrectAllocationErrorHandling.ql} | 4 ++-- ...ors.expected => IncorrectAllocationErrorHandling.expected} | 0 .../semmle/tests/IncorrectAllocationErrorHandling.qlref | 1 + .../WrongInDetectingAndHandlingMemoryAllocationErrors.qlref | 1 - 6 files changed, 4 insertions(+), 4 deletions(-) rename cpp/ql/src/experimental/Security/CWE/CWE-570/{WrongInDetectingAndHandlingMemoryAllocationErrors.cpp => IncorrectAllocationErrorHandling.cpp} (100%) rename cpp/ql/src/experimental/Security/CWE/CWE-570/{WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp => IncorrectAllocationErrorHandling.qhelp} (93%) rename cpp/ql/src/experimental/Security/CWE/CWE-570/{WrongInDetectingAndHandlingMemoryAllocationErrors.ql => IncorrectAllocationErrorHandling.ql} (98%) rename cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/{WrongInDetectingAndHandlingMemoryAllocationErrors.expected => IncorrectAllocationErrorHandling.expected} (100%) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref delete mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp similarity index 100% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp rename to cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp similarity index 93% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp rename to cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp index 9365579b44a..9e131e75d4e 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp @@ -17,7 +17,7 @@ make sure to handle the possibility of null pointers if new(std::nothrow)
    - + diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql similarity index 98% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql rename to cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 58221e07f5c..66bbb8bfad7 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -1,8 +1,8 @@ /** - * @name Detect And Handle Memory Allocation Errors + * @name Incorrect allocation-error handling * @description `operator new` throws an exception on allocation failures, while `operator new(std::nothrow)` returns a null pointer. Mixing up these two failure conditions can result in unexpected behavior. * @kind problem - * @id cpp/detect-and-handle-memory-allocation-errors + * @id cpp/incorrect-allocation-error-handling * @problem.severity warning * @precision medium * @tags correctness diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected similarity index 100% rename from cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected rename to cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref new file mode 100644 index 00000000000..4bba5039aea --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref deleted file mode 100644 index fc3252ef122..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql From 42b8f923be0f5977789f8780b68d4aa9140967ff Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 14:30:43 +0200 Subject: [PATCH 212/550] C++: Call noexcept constructor instead. --- .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 8d27c4b727d..0bb241ab5c7 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -202,7 +202,7 @@ void bad_new_with_nonthrowing_call() { try { int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw - Foo f(12); + Foo f; if(f) { } } catch(...) { } } \ No newline at end of file From 95e65dec8faf1eb0836f09c3469a96a6fd1f8b1e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 14:35:27 +0200 Subject: [PATCH 213/550] C++: Make sure a CatchBlock that catches a const std::bad_alloc& is also a BadAllocCatchBlock. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 66bbb8bfad7..dcf5a994de4 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -139,7 +139,8 @@ class BadAllocType extends Class { */ class BadAllocCatchBlock extends CatchBlock { BadAllocCatchBlock() { - this.getParameter().getUnspecifiedType() = any(BadAllocType badAlloc).getADerivedClass*() + this.getParameter().getUnspecifiedType().stripType() = + any(BadAllocType badAlloc).getADerivedClass*() or not exists(this.getParameter()) } From 167dc86f7a7e6d920e36e520f1d870afee55b421 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 14:36:35 +0200 Subject: [PATCH 214/550] C++: Accept test changes. --- .../semmle/tests/IncorrectAllocationErrorHandling.expected | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index edd08b9802d..b0af0f72e32 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -10,5 +10,8 @@ | test.cpp:63:8:63:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:64:7:64:19 | ... != ... | This check | | test.cpp:69:9:69:20 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:70:7:70:14 | ... != ... | This check | | test.cpp:75:11:75:22 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:76:13:76:15 | p11 | This check | +| test.cpp:92:5:92:31 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | +| test.cpp:93:15:93:41 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | +| test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | | test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | From 4463293dc445376b38e8c96530e35c573f0ed35b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 16:35:41 +0200 Subject: [PATCH 215/550] C++: Move common code from NewExpr and NewArrayExpr into the NewOrNewArrayExpr class. --- cpp/ql/src/semmle/code/cpp/exprs/Expr.qll | 38 +++++++++++------------ 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index 86f538e5d4e..f77518c2f56 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -850,6 +850,24 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { this.getAllocatorCall() .getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument()) } + + /** + * For `operator new`, this gets the call or expression that initializes the allocated object, if any. + * + * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will + * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument. + * + * For `operator new[]`, this gets the call or expression that initializes the first element of the + * array, if any. + * + * This will either be a call to the default constructor for the array's element type (as + * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized + * due to extra parentheses (as in `new int[10]()`). + * + * At runtime, the constructor will be called once for each element in the array, but the + * constructor call only exists once in the AST. + */ + final Expr getInitializer() { result = this.getChild(1) } } /** @@ -871,14 +889,6 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr { override Type getAllocatedType() { new_allocated_type(underlyingElement(this), unresolveElement(result)) } - - /** - * Gets the call or expression that initializes the allocated object, if any. - * - * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will - * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument. - */ - Expr getInitializer() { result = this.getChild(1) } } /** @@ -909,18 +919,6 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { result = getType().getUnderlyingType().(PointerType).getBaseType() } - /** - * Gets the call or expression that initializes the first element of the array, if any. - * - * This will either be a call to the default constructor for the array's element type (as - * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized - * due to extra parentheses (as in `new int[10]()`). - * - * At runtime, the constructor will be called once for each element in the array, but the - * constructor call only exists once in the AST. - */ - Expr getInitializer() { result = this.getChild(1) } - /** * Gets the extent of the non-constant array dimension, if any. * From 47a419a5f1bcb9022145aa1a982d998e776b2e17 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 16:37:26 +0200 Subject: [PATCH 216/550] C++: Respond to review comments. First: Avoid using locations to detect constructor and destructor calls. Second: Include missing statements in stmtMayThrow. --- .../IncorrectAllocationErrorHandling.ql | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index dcf5a994de4..9a091663848 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -23,16 +23,20 @@ class DeleteOrDeleteArrayExpr extends Expr { DeallocationFunction getDeallocator() { result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()] } + + Destructor getDestructor() { + result = [this.(DeleteExpr).getDestructor(), this.(DeleteArrayExpr).getDestructor()] + } } /** Gets the `Constructor` invoked when `newExpr` allocates memory. */ Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) { - result.getACallToThisFunction().getLocation() = newExpr.getLocation() + result.getACallToThisFunction() = newExpr.getInitializer() } /** Gets the `Destructor` invoked when `deleteExpr` deallocates memory. */ Destructor getDestructorForDeallocation(DeleteOrDeleteArrayExpr deleteExpr) { - result.getACallToThisFunction().getLocation() = deleteExpr.getLocation() + result = deleteExpr.getDestructor() } /** Holds if the evaluation of `newExpr` may throw an exception. */ @@ -63,15 +67,45 @@ predicate stmtMayThrow(Stmt stmt) { or convertedExprMayThrow(stmt.(ExprStmt).getExpr()) or + convertedExprMayThrow(stmt.(DeclStmt).getADeclaration().(Variable).getInitializer().getExpr()) + or exists(IfStmt ifStmt | ifStmt = stmt | convertedExprMayThrow(ifStmt.getCondition()) or stmtMayThrow([ifStmt.getThen(), ifStmt.getElse()]) ) or + exists(ConstexprIfStmt constIfStmt | constIfStmt = stmt | + stmtMayThrow([constIfStmt.getThen(), constIfStmt.getElse()]) + ) + or exists(Loop loop | loop = stmt | convertedExprMayThrow(loop.getCondition()) or stmtMayThrow(loop.getStmt()) ) + or + // The case for `Loop` already checked the condition and the statement. + convertedExprMayThrow(stmt.(RangeBasedForStmt).getUpdate()) + or + // The case for `Loop` already checked the condition and the statement. + exists(ForStmt forStmt | forStmt = stmt | + stmtMayThrow(forStmt.getInitialization()) + or + convertedExprMayThrow(forStmt.getUpdate()) + ) + or + exists(SwitchStmt switchStmt | switchStmt = stmt | + convertedExprMayThrow(switchStmt.getExpr()) or + stmtMayThrow(switchStmt.getStmt()) + ) + or + stmtMayThrow(stmt.(SwitchCase).getAStmt()) + or + // NOTE: We don't include `TryStmt` as those exceptions are not "observable" outside the function. + stmtMayThrow(stmt.(Handler).getBlock()) + or + convertedExprMayThrow(stmt.(CoReturnStmt).getExpr()) + or + convertedExprMayThrow(stmt.(ReturnStmt).getExpr()) } /** Holds if the evaluation of `e` (including conversions) may throw an exception. */ From 7b8a51f995be2297265cb087cc19868bfa02cfc3 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 16:56:11 +0200 Subject: [PATCH 217/550] C++: Add test with missing result. --- .../Security/CWE/CWE-570/semmle/tests/test.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 0bb241ab5c7..dde820d5df6 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -205,4 +205,10 @@ void bad_new_with_nonthrowing_call() { Foo f; if(f) { } } catch(...) { } -} \ No newline at end of file +} + +void bad_new_catch_baseclass_of_bad_alloc() { + try { + int* p = new(std::nothrow) int; // BAD [NOT DETECTED] + } catch(const std::exception&) { } +} From c12837cff0bd1ba92a08a0898891b0519ed2db2f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 16:57:09 +0200 Subject: [PATCH 218/550] C++: Fix false negative. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 4 ++-- .../semmle/tests/IncorrectAllocationErrorHandling.expected | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 9a091663848..c1f457f47e1 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -168,13 +168,13 @@ class BadAllocType extends Class { } /** - * A catch block that catches a `std::bad_alloc` (or any of its subclasses), or a catch + * A catch block that catches a `std::bad_alloc` (or any of its superclasses), or a catch * block that catches every exception (i.e., `catch(...)`). */ class BadAllocCatchBlock extends CatchBlock { BadAllocCatchBlock() { this.getParameter().getUnspecifiedType().stripType() = - any(BadAllocType badAlloc).getADerivedClass*() + any(BadAllocType badAlloc).getABaseClass*() or not exists(this.getParameter()) } diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index b0af0f72e32..4720e02b381 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -15,3 +15,4 @@ | test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | | test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | +| test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | From e0606d61b613844d8a9355b718515a38c6729999 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 16:58:49 +0200 Subject: [PATCH 219/550] C++: Fix qldoc. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index c1f457f47e1..557d73edd0d 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -181,8 +181,9 @@ class BadAllocCatchBlock extends CatchBlock { } /** - * Holds if `newExpr` will not throw an exception, but is embedded in a `try` statement - * with a catch block `catchBlock` that catches an `std::bad_alloc` exception. + * Holds if `newExpr` is embedded in a `try` statement with a catch block `catchBlock` that + * catches a `std::bad_alloc` exception, but nothing in the `try` block (including the `newExpr`) + * will throw that exception. */ predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) { exists(TryStmt try | From d1eb7747371815275c47d0947cf5c1969cb8984c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 17:03:42 +0200 Subject: [PATCH 220/550] C++: Remove implied conjunction. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 557d73edd0d..6bb0182a79e 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -191,8 +191,7 @@ predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchB not convertedExprMayThrow(cand) ) and try.getACatchClause() = catchBlock and - newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() and - not newMayThrow(newExpr) + newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() ) } From d95ef89cee68f558e3e5fbc8fb17c4e105527962 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 13 Apr 2021 12:31:42 -0700 Subject: [PATCH 221/550] C++: add test for IR alias analysis soundness --- .../ir/ssa/aliased_ssa_ir.expected | 87 +++++++++++++++++++ .../ir/ssa/aliased_ssa_ir_unsound.expected | 84 ++++++++++++++++++ cpp/ql/test/library-tests/ir/ssa/ssa.cpp | 20 +++++ .../ir/ssa/unaliased_ssa_ir.expected | 74 ++++++++++++++++ .../ir/ssa/unaliased_ssa_ir_unsound.expected | 74 ++++++++++++++++ 5 files changed, 339 insertions(+) diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 76de50dd792..c83ed76902e 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1502,3 +1502,90 @@ ssa.cpp: # 310| v310_12(void) = ReturnVoid : # 310| v310_13(void) = AliasedUse : m310_3 # 310| v310_14(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| m319_2(unknown) = AliasedDefinition : +# 319| m319_3(unknown) = InitializeNonLocal : +# 319| m319_4(unknown) = Chi : total:m319_2, partial:m319_3 +# 319| r319_5(glval) = VariableAddress[s] : +# 319| m319_6(char *) = InitializeParameter[s] : &:r319_5 +# 319| r319_7(char *) = Load[s] : &:r319_5, m319_6 +# 319| m319_8(unknown) = InitializeIndirection[s] : &:r319_7 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| m321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 321| m321_3(unknown) = Chi : total:m319_4, partial:m321_2 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| m322_3(unknown) = Chi : total:m321_3, partial:m322_2 +# 322| r322_4(glval) = VariableAddress[ptr2] : +# 322| m322_5(char **) = Uninitialized[ptr2] : &:r322_4 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 325| m325_5(unknown) = Chi : total:m322_3, partial:m325_4 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, m325_4 +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_6 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m319_8 +# 327| m327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 327| m327_13(unknown) = Chi : total:m325_5, partial:m327_12 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| m329_5(unknown) = ^CallSideEffect : ~m327_13 +# 329| m329_6(unknown) = Chi : total:m327_13, partial:m329_5 +# 329| v329_7(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m329_6 +# 329| m329_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 329| m329_9(unknown) = Chi : total:m329_6, partial:m329_8 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m329_6 +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| m330_5(unknown) = ^CallSideEffect : ~m329_9 +# 330| m330_6(unknown) = Chi : total:m329_9, partial:m330_5 +# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m330_6 +# 330| m330_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 330| m330_9(unknown) = Chi : total:m330_6, partial:m330_8 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| m331_5(unknown) = ^CallSideEffect : ~m330_9 +# 331| m331_6(unknown) = Chi : total:m330_9, partial:m331_5 +# 331| v331_7(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m331_6 +# 331| m331_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 331| m331_9(unknown) = Chi : total:m331_6, partial:m331_8 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m331_9 +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| m332_6(unknown) = ^CallSideEffect : ~m331_9 +# 332| m332_7(unknown) = Chi : total:m331_9, partial:m332_6 +# 332| v332_8(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m332_7 +# 332| m332_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 332| m332_10(unknown) = Chi : total:m332_7, partial:m332_9 +# 333| v333_1(void) = NoOp : +# 319| v319_9(void) = ReturnIndirection[s] : &:r319_7, m319_8 +# 319| v319_10(void) = ReturnVoid : +# 319| v319_11(void) = AliasedUse : ~m332_10 +# 319| v319_12(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index ab79d20214a..839e85ec4b5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1495,3 +1495,87 @@ ssa.cpp: # 310| v310_12(void) = ReturnVoid : # 310| v310_13(void) = AliasedUse : m310_3 # 310| v310_14(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| m319_2(unknown) = AliasedDefinition : +# 319| m319_3(unknown) = InitializeNonLocal : +# 319| m319_4(unknown) = Chi : total:m319_2, partial:m319_3 +# 319| r319_5(glval) = VariableAddress[s] : +# 319| m319_6(char *) = InitializeParameter[s] : &:r319_5 +# 319| r319_7(char *) = Load[s] : &:r319_5, m319_6 +# 319| m319_8(unknown) = InitializeIndirection[s] : &:r319_7 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| m321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, m325_4 +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_6 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m319_8 +# 327| m327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 327| m327_13(unknown) = Chi : total:m319_4, partial:m327_12 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| m329_5(unknown) = ^CallSideEffect : ~m327_13 +# 329| m329_6(unknown) = Chi : total:m327_13, partial:m329_5 +# 329| v329_7(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m321_2 +# 329| m329_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 329| m329_9(char[1024]) = Chi : total:m321_2, partial:m329_8 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, m325_4 +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| m330_5(unknown) = ^CallSideEffect : ~m329_6 +# 330| m330_6(unknown) = Chi : total:m329_6, partial:m330_5 +# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m329_9 +# 330| m330_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 330| m330_9(char[1024]) = Chi : total:m329_9, partial:m330_8 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| m331_5(unknown) = ^CallSideEffect : ~m330_6 +# 331| m331_6(unknown) = Chi : total:m330_6, partial:m331_5 +# 331| v331_7(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m325_4 +# 331| m331_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 331| m331_9(char *) = Chi : total:m325_4, partial:m331_8 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, m331_9 +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| m332_6(unknown) = ^CallSideEffect : ~m331_6 +# 332| m332_7(unknown) = Chi : total:m331_6, partial:m332_6 +# 332| v332_8(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m332_7 +# 332| m332_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 332| m332_10(unknown) = Chi : total:m332_7, partial:m332_9 +# 333| v333_1(void) = NoOp : +# 319| v319_9(void) = ReturnIndirection[s] : &:r319_7, m319_8 +# 319| v319_10(void) = ReturnVoid : +# 319| v319_11(void) = AliasedUse : ~m332_10 +# 319| v319_12(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index 34886b1f343..73aefa17dae 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -311,3 +311,23 @@ class ThisAliasTest { this->x = arg; } }; + +void sink(char **); +void sink(char *); + +// This test case comes from DefaultTaintTracking. +void DoubleIndirectionEscapes(char *s) +{ + char buffer[1024]; + char *ptr1, **ptr2; + char *ptr3, **ptr4; + + ptr1 = buffer; + ptr2 = &ptr1; + memcpy(*ptr2, s, 1024); + + sink(buffer); // $ MISSING: ast,ir + sink(ptr1); // $ ast MISSING: ir + sink(ptr2); // $ SPURIOUS: ast + sink(*ptr2); // $ ast MISSING: ir +} diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 0b257db98a4..b396d50c366 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -1376,3 +1376,77 @@ ssa.cpp: # 310| v310_11(void) = ReturnVoid : # 310| v310_12(void) = AliasedUse : ~m? # 310| v310_13(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| mu319_2(unknown) = AliasedDefinition : +# 319| mu319_3(unknown) = InitializeNonLocal : +# 319| r319_4(glval) = VariableAddress[s] : +# 319| m319_5(char *) = InitializeParameter[s] : &:r319_4 +# 319| r319_6(char *) = Load[s] : &:r319_4, m319_5 +# 319| mu319_7(unknown) = InitializeIndirection[s] : &:r319_6 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| mu321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| mu322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| mu325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, ~m? +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_5 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m? +# 327| mu327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| mu329_5(unknown) = ^CallSideEffect : ~m? +# 329| v329_6(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m? +# 329| mu329_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m? +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| mu330_5(unknown) = ^CallSideEffect : ~m? +# 330| v330_6(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m? +# 330| mu330_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| mu331_5(unknown) = ^CallSideEffect : ~m? +# 331| v331_6(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m? +# 331| mu331_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m? +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| mu332_6(unknown) = ^CallSideEffect : ~m? +# 332| v332_7(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m? +# 332| mu332_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 333| v333_1(void) = NoOp : +# 319| v319_8(void) = ReturnIndirection[s] : &:r319_6, ~m? +# 319| v319_9(void) = ReturnVoid : +# 319| v319_10(void) = AliasedUse : ~m? +# 319| v319_11(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 0b257db98a4..c48cd7a7186 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1376,3 +1376,77 @@ ssa.cpp: # 310| v310_11(void) = ReturnVoid : # 310| v310_12(void) = AliasedUse : ~m? # 310| v310_13(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| mu319_2(unknown) = AliasedDefinition : +# 319| mu319_3(unknown) = InitializeNonLocal : +# 319| r319_4(glval) = VariableAddress[s] : +# 319| m319_5(char *) = InitializeParameter[s] : &:r319_4 +# 319| r319_6(char *) = Load[s] : &:r319_4, m319_5 +# 319| mu319_7(unknown) = InitializeIndirection[s] : &:r319_6 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| mu321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, ~m? +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_5 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m? +# 327| mu327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| mu329_5(unknown) = ^CallSideEffect : ~m? +# 329| v329_6(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m? +# 329| mu329_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, m325_4 +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| mu330_5(unknown) = ^CallSideEffect : ~m? +# 330| v330_6(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m? +# 330| mu330_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| mu331_5(unknown) = ^CallSideEffect : ~m? +# 331| v331_6(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m? +# 331| mu331_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m? +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| mu332_6(unknown) = ^CallSideEffect : ~m? +# 332| v332_7(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m? +# 332| mu332_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 333| v333_1(void) = NoOp : +# 319| v319_8(void) = ReturnIndirection[s] : &:r319_6, ~m? +# 319| v319_9(void) = ReturnVoid : +# 319| v319_10(void) = AliasedUse : ~m? +# 319| v319_11(void) = ExitFunction : From a9d7990596540d7f56035bd51796c378c29711ba Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 17 Mar 2021 17:41:25 -0700 Subject: [PATCH 222/550] C++: make unaliased_ssa IR stage sound --- .../implementation/aliased_ssa/internal/AliasAnalysis.qll | 4 ++++ .../aliased_ssa/internal/AliasConfiguration.qll | 8 ++++++++ .../unaliased_ssa/internal/AliasAnalysis.qll | 4 ++++ .../unaliased_ssa/internal/AliasConfiguration.qll | 8 ++++++++ .../annotate_sinks_only/tainted.expected | 2 ++ .../library-tests/ir/ssa/aliased_ssa_ir_unsound.expected | 8 ++++---- .../ir/ssa/unaliased_ssa_ir_unsound.expected | 6 +++--- .../unaliased_ssa/internal/AliasAnalysis.qll | 4 ++++ .../unaliased_ssa/internal/AliasConfiguration.qll | 8 ++++++++ 9 files changed, 45 insertions(+), 7 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..ce2ebf5ce14 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -325,6 +325,10 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + exists(Configuration::StageEscapeConfiguration config | + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + ) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index 866afb64b31..d03e424abd7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -138,3 +138,11 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { final override predicate alwaysEscapes() { none() } } + +class StageEscapeConfiguration extends string { + StageEscapeConfiguration() { + this = "StageEscapeConfiguration (aliased_ssa)" + } + + predicate useSoundEscapeAnalysis() { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..ce2ebf5ce14 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -325,6 +325,10 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + exists(Configuration::StageEscapeConfiguration config | + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + ) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 5be476e12ee..2ca333ad25c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -14,3 +14,11 @@ class Allocation extends IRAutomaticVariable { none() } } + +class StageEscapeConfiguration extends string { + StageEscapeConfiguration() { + this = "StageEscapeConfiguration (unaliased_ssa)" + } + + predicate useSoundEscapeAnalysis() { any() } +} diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected index e69de29bb2d..8babe64dfc1 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected @@ -0,0 +1,2 @@ +| defaulttainttracking.cpp:190:14:190:24 | // $ ast,ir | Missing result:ir= | +| defaulttainttracking.cpp:193:14:193:24 | // $ ast,ir | Missing result:ir= | diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index 839e85ec4b5..81c80c6b4c7 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1552,15 +1552,15 @@ ssa.cpp: # 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 # 330| m330_5(unknown) = ^CallSideEffect : ~m329_6 # 330| m330_6(unknown) = Chi : total:m329_6, partial:m330_5 -# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m329_9 +# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m330_6 # 330| m330_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 -# 330| m330_9(char[1024]) = Chi : total:m329_9, partial:m330_8 +# 330| m330_9(unknown) = Chi : total:m330_6, partial:m330_8 # 331| r331_1(glval) = FunctionAddress[sink] : # 331| r331_2(glval) = VariableAddress[ptr2] : # 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 # 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 -# 331| m331_5(unknown) = ^CallSideEffect : ~m330_6 -# 331| m331_6(unknown) = Chi : total:m330_6, partial:m331_5 +# 331| m331_5(unknown) = ^CallSideEffect : ~m330_9 +# 331| m331_6(unknown) = Chi : total:m330_9, partial:m331_5 # 331| v331_7(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m325_4 # 331| m331_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 # 331| m331_9(char *) = Chi : total:m325_4, partial:m331_8 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index c48cd7a7186..b396d50c366 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1389,7 +1389,7 @@ ssa.cpp: # 321| r321_1(glval) = VariableAddress[buffer] : # 321| mu321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 # 322| r322_1(glval) = VariableAddress[ptr1] : -# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| mu322_2(char *) = Uninitialized[ptr1] : &:r322_1 # 322| r322_3(glval) = VariableAddress[ptr2] : # 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 # 323| r323_1(glval) = VariableAddress[ptr3] : @@ -1399,7 +1399,7 @@ ssa.cpp: # 325| r325_1(glval) = VariableAddress[buffer] : # 325| r325_2(char *) = Convert : r325_1 # 325| r325_3(glval) = VariableAddress[ptr1] : -# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 325| mu325_4(char *) = Store[ptr1] : &:r325_3, r325_2 # 326| r326_1(glval) = VariableAddress[ptr1] : # 326| r326_2(char **) = CopyValue : r326_1 # 326| r326_3(glval) = VariableAddress[ptr2] : @@ -1425,7 +1425,7 @@ ssa.cpp: # 329| mu329_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 # 330| r330_1(glval) = FunctionAddress[sink] : # 330| r330_2(glval) = VariableAddress[ptr1] : -# 330| r330_3(char *) = Load[ptr1] : &:r330_2, m325_4 +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m? # 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 # 330| mu330_5(unknown) = ^CallSideEffect : ~m? # 330| v330_6(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m? diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..ce2ebf5ce14 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -325,6 +325,10 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + exists(Configuration::StageEscapeConfiguration config | + config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + ) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 5be476e12ee..2ca333ad25c 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -14,3 +14,11 @@ class Allocation extends IRAutomaticVariable { none() } } + +class StageEscapeConfiguration extends string { + StageEscapeConfiguration() { + this = "StageEscapeConfiguration (unaliased_ssa)" + } + + predicate useSoundEscapeAnalysis() { any() } +} From deff5c3af15b5c04677c9c0a9a71bc788b1c4e39 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 10 Mar 2021 14:45:46 -0800 Subject: [PATCH 223/550] C++: Reuse SSA from earlier stages This refactors the SSA stages of the IR so that instructions which have a modeled memory result in the unaliased SSA stage do not have SSA recomputed in the aliased SSA stage. --- .../aliased_ssa/Instruction.qll | 8 ++ .../ir/implementation/aliased_ssa/Operand.qll | 9 +- .../aliased_ssa/internal/AliasAnalysis.qll | 5 + .../internal/AliasConfiguration.qll | 6 +- .../aliased_ssa/internal/AliasedSSA.qll | 13 +++ .../aliased_ssa/internal/SSAConstruction.qll | 100 +++++++++++++++++- .../implementation/internal/TInstruction.qll | 12 ++- .../ir/implementation/internal/TOperand.qll | 30 +++++- .../cpp/ir/implementation/raw/Instruction.qll | 8 ++ .../cpp/ir/implementation/raw/Operand.qll | 9 +- .../unaliased_ssa/Instruction.qll | 8 ++ .../implementation/unaliased_ssa/Operand.qll | 9 +- .../unaliased_ssa/internal/AliasAnalysis.qll | 5 + .../internal/SSAConstruction.qll | 100 +++++++++++++++++- .../unaliased_ssa/internal/SimpleSSA.qll | 19 +++- .../implementation/internal/TInstruction.qll | 12 ++- .../unaliased_ssa/internal/AliasAnalysis.qll | 5 + .../internal/SSAConstruction.qll | 20 ++++ .../unaliased_ssa/internal/SimpleSSA.qll | 19 +++- 19 files changed, 375 insertions(+), 22 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index e335080ad6b..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index a2ce0662dc2..760f2b5b39d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -31,7 +31,8 @@ class Operand extends TStageOperand { exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) ) or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index ce2ebf5ce14..83492e6ab79 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | + config.useSoundEscapeAnalysis() + ) ) ) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index d03e424abd7..3be9dc581d7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -2,9 +2,13 @@ private import AliasConfigurationInternal private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR private import cpp private import AliasAnalysis +private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA as UnaliasedSSA private newtype TAllocation = - TVariableAllocation(IRVariable var) or + TVariableAllocation(IRVariable var) { + // Only model variables that were not already handled in unaliased SSA. + not UnaliasedSSA::canReuseSSAForVariable(var) + } or TIndirectParameterAllocation(IRAutomaticVariable var) { exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var) } or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index fdabee2affe..d8e6bba31a7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -3,6 +3,7 @@ import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.IRCppLanguage as Language private import semmle.code.cpp.Print private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR +private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as OldSSA private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private import semmle.code.cpp.ir.internal.IntegerInterval as Interval private import semmle.code.cpp.ir.implementation.internal.OperandTag @@ -131,6 +132,8 @@ abstract class MemoryLocation extends TMemoryLocation { * with automatic storage duration). */ predicate isAlwaysAllocatedOnStack() { none() } + + final predicate canReuseSSA() { any() } } /** @@ -562,10 +565,19 @@ private Overlap getVariableMemoryLocationOverlap( use.getEndBitOffset()) } +/** + * Holds if the def/use information for the result of `instr` can be reused from the previous + * iteration of the IR. + */ +predicate canReuseSSAForOldResult(Instruction instr) { + OldSSA::canReuseSSAForMemoryResult(instr) +} + bindingset[result, b] private boolean unbindBool(boolean b) { result != b.booleanNot() } MemoryLocation getResultMemoryLocation(Instruction instr) { + not(canReuseSSAForOldResult(instr)) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = instr.getResultMemoryAccess() and (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and @@ -598,6 +610,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { } MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { + not(canReuseSSAForOldResult(operand.getAnyDef())) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = operand.getMemoryAccess() and (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 1ba19b4a4c3..6e4440f253c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -58,9 +58,28 @@ private module Cached { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) + } + cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +136,30 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + /*or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = combineOverlap(phiOperandOverlap, originalOverlap) + ) */ + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +191,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +263,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +292,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -248,8 +313,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + exists(OldBlock oldBlock | ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,6 +404,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } @@ -862,6 +934,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index e16b71733b5..e7dfea40313 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -55,6 +55,11 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TRawInstruction reusedPhiInstruction( + TRawInstruction blockStartInstr) { + none() + } + class TChiInstruction = TUnaliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { @@ -75,7 +80,7 @@ module UnaliasedSSAInstructions { * a class alias. */ module AliasedSSAInstructions { - class TPhiInstruction = TAliasedSSAPhiInstruction; + class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction; TPhiInstruction phiInstruction( TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation @@ -83,6 +88,11 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TPhiInstruction reusedPhiInstruction( + TRawInstruction blockStartInstr) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) + } + class TChiInstruction = TAliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll index 1e132463cdd..69e68d97adc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll @@ -84,7 +84,7 @@ private module Shared { } } -/** +/** * Provides wrappers for the constructors of each branch of `TOperand` that is used by the * raw IR stage. * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via @@ -109,6 +109,12 @@ module RawOperands { none() } + TPhiOperand reusedPhiOperand( + Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock, + Overlap overlap + ) { + none() + } /** * Returns the Chi operand with the specified parameters. */ @@ -140,6 +146,12 @@ module UnaliasedSSAOperands { result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) } + TPhiOperand reusedPhiOperand( + Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, + Unaliased::IRBlock predecessorBlock, Overlap overlap + ) { + none() + } /** * Returns the Chi operand with the specified parameters. */ @@ -155,7 +167,7 @@ module UnaliasedSSAOperands { module AliasedSSAOperands { import Shared - class TPhiOperand = Internal::TAliasedPhiOperand; + class TPhiOperand = Internal::TAliasedPhiOperand or Internal::TUnaliasedPhiOperand; class TChiOperand = Internal::TAliasedChiOperand; @@ -165,12 +177,24 @@ module AliasedSSAOperands { * Returns the Phi operand with the specified parameters. */ TPhiOperand phiOperand( - TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr, + Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr, Aliased::IRBlock predecessorBlock, Overlap overlap ) { result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) } + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr, + Aliased::IRBlock predecessorBlock, Overlap overlap + ) { + exists(Unaliased::IRBlock oldBlock | + predecessorBlock.getFirstInstruction() = oldBlock.getFirstInstruction() and + result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, oldBlock, overlap) + ) + } /** * Returns the Chi operand with the specified parameters. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index e335080ad6b..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index a2ce0662dc2..760f2b5b39d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -31,7 +31,8 @@ class Operand extends TStageOperand { exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) ) or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index e335080ad6b..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index a2ce0662dc2..760f2b5b39d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -31,7 +31,8 @@ class Operand extends TStageOperand { exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) ) or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index ce2ebf5ce14..83492e6ab79 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | + config.useSoundEscapeAnalysis() + ) ) ) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 1ba19b4a4c3..6e4440f253c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -58,9 +58,28 @@ private module Cached { result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) } + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) + } + cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +136,30 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + /*or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = combineOverlap(phiOperandOverlap, originalOverlap) + ) */ + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +191,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +263,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +292,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -248,8 +313,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + exists(OldBlock oldBlock | ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,6 +404,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } @@ -862,6 +934,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index a7b9160bdc7..906699b6394 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy * variable if its address never escapes and all reads and writes of that variable access the entire * variable using the original type of the variable. */ -private predicate isVariableModeled(Allocation var) { +predicate isVariableModeled(Allocation var) { not allocationEscapes(var) and forall(Instruction instr, AddressOperand addrOperand, IRType type | addrOperand = instr.getResultAddressOperand() and @@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) { ) } +/** + * Holds if the SSA use/def chain for the specified variable can be safely reused by later + * iterations of SSA construction. This will hold only if we modeled the variable soundly, so that + * subsequent iterations will recompute SSA for any variable that we assumed did not escape, but + * actually would have escaped if we had used a sound escape analysis. + */ +predicate canReuseSSAForVariable(IRAutomaticVariable var) { + isVariableModeled(var) and + not allocationEscapes(var) +} + private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } @@ -57,6 +68,12 @@ class MemoryLocation extends TMemoryLocation { final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } + + final predicate canReuseSSA() { canReuseSSAForVariable(var) } +} + +predicate canReuseSSAForOldResult(Instruction instr) { + none() } /** diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll index e16b71733b5..911d9af78fa 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll @@ -55,6 +55,11 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TPhiInstruction reusedPhiInstruction( + TRawInstruction blockStartInstr) { + none() + } + class TChiInstruction = TUnaliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { @@ -75,7 +80,7 @@ module UnaliasedSSAInstructions { * a class alias. */ module AliasedSSAInstructions { - class TPhiInstruction = TAliasedSSAPhiInstruction; + class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction; TPhiInstruction phiInstruction( TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation @@ -83,6 +88,11 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TPhiInstruction reusedPhiInstruction( + TRawInstruction blockStartInstr) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) + } + class TChiInstruction = TAliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index ce2ebf5ce14..83492e6ab79 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | + config.useSoundEscapeAnalysis() + ) ) ) or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 1ba19b4a4c3..262865fa1d1 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -862,6 +862,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index a7b9160bdc7..906699b6394 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy * variable if its address never escapes and all reads and writes of that variable access the entire * variable using the original type of the variable. */ -private predicate isVariableModeled(Allocation var) { +predicate isVariableModeled(Allocation var) { not allocationEscapes(var) and forall(Instruction instr, AddressOperand addrOperand, IRType type | addrOperand = instr.getResultAddressOperand() and @@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) { ) } +/** + * Holds if the SSA use/def chain for the specified variable can be safely reused by later + * iterations of SSA construction. This will hold only if we modeled the variable soundly, so that + * subsequent iterations will recompute SSA for any variable that we assumed did not escape, but + * actually would have escaped if we had used a sound escape analysis. + */ +predicate canReuseSSAForVariable(IRAutomaticVariable var) { + isVariableModeled(var) and + not allocationEscapes(var) +} + private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } @@ -57,6 +68,12 @@ class MemoryLocation extends TMemoryLocation { final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } + + final predicate canReuseSSA() { canReuseSSAForVariable(var) } +} + +predicate canReuseSSAForOldResult(Instruction instr) { + none() } /** From 8bc7e5993e436089c48b54d7cfcfed0c26ea7588 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 13 Apr 2021 16:49:41 -0700 Subject: [PATCH 224/550] autoformat and sync C++ files --- .../ir/implementation/aliased_ssa/Operand.qll | 9 ++++-- .../aliased_ssa/internal/AliasAnalysis.qll | 4 +-- .../internal/AliasConfiguration.qll | 4 +-- .../aliased_ssa/internal/AliasedSSA.qll | 8 ++--- .../aliased_ssa/internal/SSAConstruction.qll | 30 +++++++++++-------- .../implementation/internal/TInstruction.qll | 8 ++--- .../ir/implementation/internal/TOperand.qll | 5 +++- .../cpp/ir/implementation/raw/Operand.qll | 9 ++++-- .../implementation/unaliased_ssa/Operand.qll | 9 ++++-- .../unaliased_ssa/internal/AliasAnalysis.qll | 4 +-- .../internal/AliasConfiguration.qll | 4 +-- .../internal/SSAConstruction.qll | 30 +++++++++++-------- .../unaliased_ssa/internal/SimpleSSA.qll | 4 +-- 13 files changed, 66 insertions(+), 62 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index 760f2b5b39d..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -28,12 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | this = phiOperand(use, def, predecessorBlock, _) or this = reusedPhiOperand(use, def, predecessorBlock, _) - ) or + ) + or exists(Instruction use | this = chiOperand(use, _)) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 83492e6ab79..1198c828ecb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -92,9 +92,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType or instr instanceof CallInstruction and - not exists(IREscapeAnalysisConfiguration config | - config.useSoundEscapeAnalysis() - ) + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index 3be9dc581d7..236db922646 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -144,9 +144,7 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { } class StageEscapeConfiguration extends string { - StageEscapeConfiguration() { - this = "StageEscapeConfiguration (aliased_ssa)" - } + StageEscapeConfiguration() { this = "StageEscapeConfiguration (aliased_ssa)" } predicate useSoundEscapeAnalysis() { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index d8e6bba31a7..74638b6ec1d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -569,15 +569,13 @@ private Overlap getVariableMemoryLocationOverlap( * Holds if the def/use information for the result of `instr` can be reused from the previous * iteration of the IR. */ -predicate canReuseSSAForOldResult(Instruction instr) { - OldSSA::canReuseSSAForMemoryResult(instr) -} +predicate canReuseSSAForOldResult(Instruction instr) { OldSSA::canReuseSSAForMemoryResult(instr) } bindingset[result, b] private boolean unbindBool(boolean b) { result != b.booleanNot() } MemoryLocation getResultMemoryLocation(Instruction instr) { - not(canReuseSSAForOldResult(instr)) and + not canReuseSSAForOldResult(instr) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = instr.getResultMemoryAccess() and (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and @@ -610,7 +608,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { } MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { - not(canReuseSSAForOldResult(operand.getAnyDef())) and + not canReuseSSAForOldResult(operand.getAnyDef()) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = operand.getMemoryAccess() and (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 6e4440f253c..343499dcc6c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -150,13 +150,16 @@ private module Cached { ( result = getNewInstruction(oldOperand.getAnyDef()) and overlap = originalOverlap - /*or - exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | - phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and - result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - overlap = combineOverlap(phiOperandOverlap, originalOverlap) - ) */ - ) + /* + * or + * exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + * phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + * result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + * overlap = combineOverlap(phiOperandOverlap, originalOverlap) + * ) + */ + + ) ) } @@ -313,12 +316,13 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldBlock oldBlock | ( - instr = getPhi(oldBlock, _) - or - // Any `Phi` that we propagated from the previous iteration stays in the same block. - getOldInstruction(instr).getBlock() = oldBlock - ) and + exists(OldBlock oldBlock | + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index e7dfea40313..4b3f19cbdde 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -55,10 +55,7 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } - TRawInstruction reusedPhiInstruction( - TRawInstruction blockStartInstr) { - none() - } + TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() } class TChiInstruction = TUnaliasedSSAChiInstruction; @@ -88,8 +85,7 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } - TPhiInstruction reusedPhiInstruction( - TRawInstruction blockStartInstr) { + TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll index 69e68d97adc..57cbb995a00 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll @@ -84,7 +84,7 @@ private module Shared { } } -/** +/** * Provides wrappers for the constructors of each branch of `TOperand` that is used by the * raw IR stage. * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via @@ -115,6 +115,7 @@ module RawOperands { ) { none() } + /** * Returns the Chi operand with the specified parameters. */ @@ -152,6 +153,7 @@ module UnaliasedSSAOperands { ) { none() } + /** * Returns the Chi operand with the specified parameters. */ @@ -195,6 +197,7 @@ module AliasedSSAOperands { result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, oldBlock, overlap) ) } + /** * Returns the Chi operand with the specified parameters. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index 760f2b5b39d..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -28,12 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | this = phiOperand(use, def, predecessorBlock, _) or this = reusedPhiOperand(use, def, predecessorBlock, _) - ) or + ) + or exists(Instruction use | this = chiOperand(use, _)) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index 760f2b5b39d..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -28,12 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | this = phiOperand(use, def, predecessorBlock, _) or this = reusedPhiOperand(use, def, predecessorBlock, _) - ) or + ) + or exists(Instruction use | this = chiOperand(use, _)) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 83492e6ab79..1198c828ecb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -92,9 +92,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType or instr instanceof CallInstruction and - not exists(IREscapeAnalysisConfiguration config | - config.useSoundEscapeAnalysis() - ) + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 2ca333ad25c..d39288cd412 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -16,9 +16,7 @@ class Allocation extends IRAutomaticVariable { } class StageEscapeConfiguration extends string { - StageEscapeConfiguration() { - this = "StageEscapeConfiguration (unaliased_ssa)" - } + StageEscapeConfiguration() { this = "StageEscapeConfiguration (unaliased_ssa)" } predicate useSoundEscapeAnalysis() { any() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 6e4440f253c..343499dcc6c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -150,13 +150,16 @@ private module Cached { ( result = getNewInstruction(oldOperand.getAnyDef()) and overlap = originalOverlap - /*or - exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | - phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and - result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - overlap = combineOverlap(phiOperandOverlap, originalOverlap) - ) */ - ) + /* + * or + * exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + * phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + * result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + * overlap = combineOverlap(phiOperandOverlap, originalOverlap) + * ) + */ + + ) ) } @@ -313,12 +316,13 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { - exists(OldBlock oldBlock | ( - instr = getPhi(oldBlock, _) - or - // Any `Phi` that we propagated from the previous iteration stays in the same block. - getOldInstruction(instr).getBlock() = oldBlock - ) and + exists(OldBlock oldBlock | + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 906699b6394..f3e02c9f6a8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -72,9 +72,7 @@ class MemoryLocation extends TMemoryLocation { final predicate canReuseSSA() { canReuseSSAForVariable(var) } } -predicate canReuseSSAForOldResult(Instruction instr) { - none() -} +predicate canReuseSSAForOldResult(Instruction instr) { none() } /** * Represents a set of `MemoryLocation`s that cannot overlap with From 1c72ea97a73b1b0008210a689894efd4503a9888 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 13 Apr 2021 17:12:51 -0700 Subject: [PATCH 225/550] C++: accept phi node reorderings in IR tests --- .../library-tests/ir/ssa/aliased_ssa_ir.expected | 14 +++++++------- .../ir/ssa/aliased_ssa_ir_unsound.expected | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index c83ed76902e..b694ef800d6 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -219,11 +219,11 @@ ssa.cpp: #-----| Goto -> Block 1 # 69| Block 1 -# 69| m69_1(char *) = Phi : from 0:m68_8, from 2:m70_6 -# 69| m69_2(int) = Phi : from 0:m68_6, from 2:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_1(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_2(char *) = Phi : from 0:m68_8, from 2:m70_6 +# 69| m69_3(int) = Phi : from 0:m68_6, from 2:m69_8 # 69| r69_4(glval) = VariableAddress[n] : -# 69| r69_5(int) = Load[n] : &:r69_4, m69_2 +# 69| r69_5(int) = Load[n] : &:r69_4, m69_3 # 69| r69_6(int) = Constant[1] : # 69| r69_7(int) = Sub : r69_5, r69_6 # 69| m69_8(int) = Store[n] : &:r69_4, r69_7 @@ -237,21 +237,21 @@ ssa.cpp: # 70| Block 2 # 70| r70_1(char) = Constant[0] : # 70| r70_2(glval) = VariableAddress[p] : -# 70| r70_3(char *) = Load[p] : &:r70_2, m69_1 +# 70| r70_3(char *) = Load[p] : &:r70_2, m69_2 # 70| r70_4(int) = Constant[1] : # 70| r70_5(char *) = PointerAdd[1] : r70_3, r70_4 # 70| m70_6(char *) = Store[p] : &:r70_2, r70_5 # 70| r70_7(char *) = CopyValue : r70_3 # 70| r70_8(glval) = CopyValue : r70_7 # 70| m70_9(char) = Store[?] : &:r70_8, r70_1 -# 70| m70_10(unknown) = Chi : total:m69_3, partial:m70_9 +# 70| m70_10(unknown) = Chi : total:m69_1, partial:m70_9 #-----| Goto (back edge) -> Block 1 # 71| Block 3 # 71| v71_1(void) = NoOp : # 68| v68_11(void) = ReturnIndirection[p] : &:r68_9, m68_10 # 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_13(void) = AliasedUse : ~m69_1 # 68| v68_14(void) = ExitFunction : # 75| void ScalarPhi(bool) diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index 81c80c6b4c7..a877587ea21 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -219,11 +219,11 @@ ssa.cpp: #-----| Goto -> Block 1 # 69| Block 1 -# 69| m69_1(char *) = Phi : from 0:m68_8, from 2:m70_6 -# 69| m69_2(int) = Phi : from 0:m68_6, from 2:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_1(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_2(char *) = Phi : from 0:m68_8, from 2:m70_6 +# 69| m69_3(int) = Phi : from 0:m68_6, from 2:m69_8 # 69| r69_4(glval) = VariableAddress[n] : -# 69| r69_5(int) = Load[n] : &:r69_4, m69_2 +# 69| r69_5(int) = Load[n] : &:r69_4, m69_3 # 69| r69_6(int) = Constant[1] : # 69| r69_7(int) = Sub : r69_5, r69_6 # 69| m69_8(int) = Store[n] : &:r69_4, r69_7 @@ -237,21 +237,21 @@ ssa.cpp: # 70| Block 2 # 70| r70_1(char) = Constant[0] : # 70| r70_2(glval) = VariableAddress[p] : -# 70| r70_3(char *) = Load[p] : &:r70_2, m69_1 +# 70| r70_3(char *) = Load[p] : &:r70_2, m69_2 # 70| r70_4(int) = Constant[1] : # 70| r70_5(char *) = PointerAdd[1] : r70_3, r70_4 # 70| m70_6(char *) = Store[p] : &:r70_2, r70_5 # 70| r70_7(char *) = CopyValue : r70_3 # 70| r70_8(glval) = CopyValue : r70_7 # 70| m70_9(char) = Store[?] : &:r70_8, r70_1 -# 70| m70_10(unknown) = Chi : total:m69_3, partial:m70_9 +# 70| m70_10(unknown) = Chi : total:m69_1, partial:m70_9 #-----| Goto (back edge) -> Block 1 # 71| Block 3 # 71| v71_1(void) = NoOp : # 68| v68_11(void) = ReturnIndirection[p] : &:r68_9, m68_10 # 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_13(void) = AliasedUse : ~m69_1 # 68| v68_14(void) = ExitFunction : # 75| void ScalarPhi(bool) From f9e0ba17e01b63ca228ae8f4a3cd9ce089fe8269 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 13 Apr 2021 17:14:41 -0700 Subject: [PATCH 226/550] C++: remove points-to expectations for reused SSA --- .../library-tests/ir/points_to/points_to.cpp | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp index 024a3190706..fa4d062c5f8 100644 --- a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp @@ -37,51 +37,51 @@ void Locals() { } void PointsTo( - int a, //$raw,ussa=a - Point& b, //$raw,ussa=b ussa=*b - Point* c, //$raw,ussa=c ussa=*c - int* d, //$raw,ussa=d ussa=*d - DerivedSI* e, //$raw,ussa=e ussa=*e - DerivedMI* f, //$raw,ussa=f ussa=*f - DerivedVI* g //$raw,ussa=g ussa=*g + int a, //$raw=a + Point& b, //$raw=b ussa=*b + Point* c, //$raw=c ussa=*c + int* d, //$raw=d ussa=*d + DerivedSI* e, //$raw=e ussa=*e + DerivedMI* f, //$raw=f ussa=*f + DerivedVI* g //$raw=g ussa=*g ) { - int i = a; //$raw,ussa=a - i = *&a; //$raw,ussa=a - i = *(&a + 0); //$raw,ussa=a - i = b.x; //$raw,ussa=b ussa=*b[0..4) - i = b.y; //$raw,ussa=b ussa=*b[4..8) - i = c->x; //$raw,ussa=c ussa=*c[0..4) - i = c->y; //$raw,ussa=c ussa=*c[4..8) - i = *d; //$raw,ussa=d ussa=*d[0..4) - i = *(d + 0); //$raw,ussa=d ussa=*d[0..4) - i = d[5]; //$raw,ussa=d ussa=*d[20..24) - i = 5[d]; //$raw,ussa=d ussa=*d[20..24) - i = d[a]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?) - i = a[d]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?) + int i = a; //$raw=a + i = *&a; //$raw=a + i = *(&a + 0); //$raw=a + i = b.x; //$raw=b ussa=*b[0..4) + i = b.y; //$raw=b ussa=*b[4..8) + i = c->x; //$raw=c ussa=*c[0..4) + i = c->y; //$raw=c ussa=*c[4..8) + i = *d; //$raw=d ussa=*d[0..4) + i = *(d + 0); //$raw=d ussa=*d[0..4) + i = d[5]; //$raw=d ussa=*d[20..24) + i = 5[d]; //$raw=d ussa=*d[20..24) + i = d[a]; //$raw=d raw=a ussa=*d[?..?) + i = a[d]; //$raw=d raw=a ussa=*d[?..?) - int* p = &b.x; //$raw,ussa=b + int* p = &b.x; //$raw=b i = *p; //$ussa=*b[0..4) - p = &b.y; //$raw,ussa=b + p = &b.y; //$raw=b i = *p; //$ussa=*b[4..8) - p = &c->x; //$raw,ussa=c + p = &c->x; //$raw=c i = *p; //$ussa=*c[0..4) - p = &c->y; //$raw,ussa=c + p = &c->y; //$raw=c i = *p; //$ussa=*c[4..8) - p = &d[5]; //$raw,ussa=d + p = &d[5]; //$raw=d i = *p; //$ussa=*d[20..24) - p = &d[a]; //$raw,ussa=d raw,ussa=a + p = &d[a]; //$raw=d raw=a i = *p; //$ussa=*d[?..?) - Point* q = &c[a]; //$raw,ussa=c raw,ussa=a + Point* q = &c[a]; //$raw=c raw=a i = q->x; //$ussa=*c[?..?) i = q->y; //$ussa=*c[?..?) - i = e->b1; //$raw,ussa=e ussa=*e[0..4) - i = e->dsi; //$raw,ussa=e ussa=*e[4..8) - i = f->b1; //$raw,ussa=f ussa=*f[0..4) - i = f->b2; //$raw,ussa=f ussa=*f[4..8) - i = f->dmi; //$raw,ussa=f ussa=*f[8..12) - i = g->b1; //$raw,ussa=g ussa=*g[?..?) - i = g->dvi; //$raw,ussa=g ussa=*g[8..12) + i = e->b1; //$raw=e ussa=*e[0..4) + i = e->dsi; //$raw=e ussa=*e[4..8) + i = f->b1; //$raw=f ussa=*f[0..4) + i = f->b2; //$raw=f ussa=*f[4..8) + i = f->dmi; //$raw=f ussa=*f[8..12) + i = g->b1; //$raw=g ussa=*g[?..?) + i = g->dvi; //$raw=g ussa=*g[8..12) } \ No newline at end of file From 86b1d032ae08cb62a9323a36076185beb1cfdd98 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 15 Apr 2021 17:06:14 -0700 Subject: [PATCH 227/550] C++: accept test regressions --- .../dataflow/fields/ir-path-flow.expected | 22 ----------------- .../UncontrolledProcessOperation.expected | 24 ------------------- 2 files changed, 46 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index e6234ca17f7..ee6b6a39bde 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -135,40 +135,24 @@ edges | by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a | | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | by_reference.cpp:128:15:128:23 | Chi | | complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:16:42:16 | f indirection [a_] | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | Chi [b_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | f indirection [b_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | -| complex.cpp:42:16:42:16 | Chi [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | -| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:42:16:42:16 | Chi [b_] | | complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | | complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:42:18:42:18 | call to a | | complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:42:16:42:16 | a output argument [b_] | | complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:43:18:43:18 | call to b | -| complex.cpp:53:12:53:12 | Chi [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] | -| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:53:12:53:12 | Chi [a_] | | complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] | | complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] | | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:14:53:17 | call to user_input | -| complex.cpp:54:12:54:12 | Chi [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] | -| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:54:12:54:12 | Chi [b_] | | complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] | | complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] | | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:14:54:17 | call to user_input | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | Chi [a_] | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | f indirection [a_] | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:55:12:55:12 | Chi [a_] | -| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | f indirection [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | | complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] | | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:14:55:17 | call to user_input | -| complex.cpp:56:12:56:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:56:12:56:12 | Chi [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] | | complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] | -| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] | | complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:56:12:56:12 | Chi [b_] | | complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] | | complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] | | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:14:56:17 | call to user_input | @@ -410,27 +394,21 @@ nodes | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | | complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | -| complex.cpp:42:16:42:16 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] | | complex.cpp:42:16:42:16 | f indirection [a_] | semmle.label | f indirection [a_] | | complex.cpp:42:16:42:16 | f indirection [b_] | semmle.label | f indirection [b_] | | complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | | complex.cpp:43:16:43:16 | f indirection [b_] | semmle.label | f indirection [b_] | | complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | -| complex.cpp:53:12:53:12 | Chi [a_] | semmle.label | Chi [a_] | | complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:53:14:53:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:54:12:54:12 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | | complex.cpp:54:14:54:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:55:12:55:12 | Chi [a_] | semmle.label | Chi [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:55:14:55:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:56:12:56:12 | Chi [a_] | semmle.label | Chi [a_] | -| complex.cpp:56:12:56:12 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:56:12:56:12 | f indirection [a_] | semmle.label | f indirection [a_] | | complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | | complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected index 5b45af80d5b..c610e346bb1 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected @@ -26,27 +26,15 @@ edges | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | (const char *)... | | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer indirection | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data indirection | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | (const char *)... | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer indirection | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | (const char *)... | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data indirection | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | (const char *)... | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer indirection | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | (const char *)... | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data indirection | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | (const char *)... | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer indirection | -| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | (const char *)... | -| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data | -| test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data indirection | | test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | (const char *)... | | test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | buffer | | test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | buffer indirection | @@ -89,11 +77,6 @@ nodes | test.cpp:62:10:62:15 | buffer | semmle.label | buffer | | test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection | | test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:63:10:63:13 | data | semmle.label | data | -| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection | -| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection | | test.cpp:76:12:76:17 | buffer | semmle.label | buffer | | test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument | | test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... | @@ -101,11 +84,6 @@ nodes | test.cpp:78:10:78:15 | buffer | semmle.label | buffer | | test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection | | test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:79:10:79:13 | data | semmle.label | data | -| test.cpp:79:10:79:13 | data indirection | semmle.label | data indirection | -| test.cpp:79:10:79:13 | data indirection | semmle.label | data indirection | | test.cpp:98:17:98:22 | buffer | semmle.label | buffer | | test.cpp:98:17:98:22 | recv output argument | semmle.label | recv output argument | | test.cpp:99:15:99:20 | (const char *)... | semmle.label | (const char *)... | @@ -124,8 +102,6 @@ nodes | test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv | | test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv | | test.cpp:62:10:62:15 | buffer | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | -| test.cpp:63:10:63:13 | data | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | | test.cpp:78:10:78:15 | buffer | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | -| test.cpp:79:10:79:13 | data | test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | | test.cpp:99:15:99:20 | buffer | test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | buffer | The value of this argument may come from $@ and is being passed to LoadLibrary | test.cpp:98:17:98:22 | buffer | buffer | | test.cpp:107:15:107:20 | buffer | test.cpp:106:17:106:22 | buffer | test.cpp:107:15:107:20 | buffer | The value of this argument may come from $@ and is being passed to LoadLibrary | test.cpp:106:17:106:22 | buffer | buffer | From 922cf640f4d34ac78b0311dc93cfb65ba10be740 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 20 Apr 2020 15:37:41 -0400 Subject: [PATCH 228/550] C++/C#: Add `combineOverlap()` predicate --- .../semmle/code/cpp/ir/internal/Overlap.qll | 35 +++++++++++++++++++ .../src/experimental/ir/internal/Overlap.qll | 35 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll index f9a0c574f8c..ca643b56cbb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll @@ -8,6 +8,16 @@ private newtype TOverlap = */ abstract class Overlap extends TOverlap { abstract string toString(); + + /** + * Gets a value representing how precise this overlap is. The higher the value, the more precise + * the overlap. The precision values are ordered as + * follows, from most to least precise: + * `MustExactlyOverlap` + * `MustTotallyOverlap` + * `MayPartiallyOverlap` + */ + abstract int getPrecision(); } /** @@ -16,6 +26,8 @@ abstract class Overlap extends TOverlap { */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } + + final override int getPrecision() { result = 0 } } /** @@ -24,6 +36,8 @@ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } + + final override int getPrecision() { result = 1 } } /** @@ -32,4 +46,25 @@ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } + + final override int getPrecision() { result = 2 } +} + +/** + * Gets the `Overlap` that best represents the relationship between two memory locations `a` and + * `c`, where `getOverlap(a, b) = previousOverlap` and `getOverlap(b, c) = newOverlap`, for some + * intermediate memory location `b`. + */ +Overlap combineOverlap(Overlap previousOverlap, Overlap newOverlap) { + // Note that it's possible that two less precise overlaps could combine to result in a more + // precise overlap. For example, both `previousOverlap` and `newOverlap` could be + // `MustTotallyOverlap` even though the actual relationship between `a` and `c` is + // `MustExactlyOverlap`. We will still return `MustTotallyOverlap` as the best conservative + // approximation we can make without additional input information. + result = + min(Overlap overlap | + overlap = [previousOverlap, newOverlap] + | + overlap order by overlap.getPrecision() + ) } diff --git a/csharp/ql/src/experimental/ir/internal/Overlap.qll b/csharp/ql/src/experimental/ir/internal/Overlap.qll index f9a0c574f8c..ca643b56cbb 100644 --- a/csharp/ql/src/experimental/ir/internal/Overlap.qll +++ b/csharp/ql/src/experimental/ir/internal/Overlap.qll @@ -8,6 +8,16 @@ private newtype TOverlap = */ abstract class Overlap extends TOverlap { abstract string toString(); + + /** + * Gets a value representing how precise this overlap is. The higher the value, the more precise + * the overlap. The precision values are ordered as + * follows, from most to least precise: + * `MustExactlyOverlap` + * `MustTotallyOverlap` + * `MayPartiallyOverlap` + */ + abstract int getPrecision(); } /** @@ -16,6 +26,8 @@ abstract class Overlap extends TOverlap { */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } + + final override int getPrecision() { result = 0 } } /** @@ -24,6 +36,8 @@ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } + + final override int getPrecision() { result = 1 } } /** @@ -32,4 +46,25 @@ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } + + final override int getPrecision() { result = 2 } +} + +/** + * Gets the `Overlap` that best represents the relationship between two memory locations `a` and + * `c`, where `getOverlap(a, b) = previousOverlap` and `getOverlap(b, c) = newOverlap`, for some + * intermediate memory location `b`. + */ +Overlap combineOverlap(Overlap previousOverlap, Overlap newOverlap) { + // Note that it's possible that two less precise overlaps could combine to result in a more + // precise overlap. For example, both `previousOverlap` and `newOverlap` could be + // `MustTotallyOverlap` even though the actual relationship between `a` and `c` is + // `MustExactlyOverlap`. We will still return `MustTotallyOverlap` as the best conservative + // approximation we can make without additional input information. + result = + min(Overlap overlap | + overlap = [previousOverlap, newOverlap] + | + overlap order by overlap.getPrecision() + ) } From 7930c4ab19b78141152604640c0119638c5efea8 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 19 Apr 2021 15:58:34 -0700 Subject: [PATCH 229/550] C++: tests for phi nodes after unreachable blocks --- .../ir/ssa/aliased_ssa_consistency.expected | 5 + .../aliased_ssa_consistency_unsound.expected | 5 + .../ir/ssa/aliased_ssa_ir.expected | 225 ++++++++++++++++ .../ir/ssa/aliased_ssa_ir_unsound.expected | 225 ++++++++++++++++ cpp/ql/test/library-tests/ir/ssa/ssa.cpp | 66 +++++ .../ir/ssa/unaliased_ssa_ir.expected | 245 ++++++++++++++++++ .../ir/ssa/unaliased_ssa_ir_unsound.expected | 245 ++++++++++++++++++ 7 files changed, 1016 insertions(+) diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected index 31e5b01229c..f0de9cc90d5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected @@ -2,6 +2,7 @@ missingOperand unexpectedOperand duplicateOperand missingPhiOperand +| ssa.cpp:398:3:398:13 | Phi: return ... | Instruction 'Phi: return ...' is missing an operand for predecessor block 'VariableAddress: y' in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | missingOperandType duplicateChiOperand sideEffectWithoutPrimary @@ -9,9 +10,13 @@ instructionWithoutSuccessor ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction +| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | +| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | memoryOperandDefinitionIsUnmodeled operandAcrossFunctions instructionWithoutUniqueBlock +| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is a member of 0 blocks in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | +| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is a member of 0 blocks in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | containsLoopOfForwardEdges lostReachability backEdgeCountMismatch diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected index 31e5b01229c..f0de9cc90d5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected @@ -2,6 +2,7 @@ missingOperand unexpectedOperand duplicateOperand missingPhiOperand +| ssa.cpp:398:3:398:13 | Phi: return ... | Instruction 'Phi: return ...' is missing an operand for predecessor block 'VariableAddress: y' in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | missingOperandType duplicateChiOperand sideEffectWithoutPrimary @@ -9,9 +10,13 @@ instructionWithoutSuccessor ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction +| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | +| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | memoryOperandDefinitionIsUnmodeled operandAcrossFunctions instructionWithoutUniqueBlock +| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is a member of 0 blocks in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | +| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is a member of 0 blocks in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | containsLoopOfForwardEdges lostReachability backEdgeCountMismatch diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index b694ef800d6..d53c52e058c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1589,3 +1589,228 @@ ssa.cpp: # 319| v319_10(void) = ReturnVoid : # 319| v319_11(void) = AliasedUse : ~m332_10 # 319| v319_12(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| m335_2(unknown) = AliasedDefinition : +# 335| m335_3(unknown) = InitializeNonLocal : +# 335| m335_4(unknown) = Chi : total:m335_2, partial:m335_3 +# 335| r335_5(glval) = VariableAddress[x] : +# 335| m335_6(int) = InitializeParameter[x] : &:r335_5 +# 335| r335_7(glval) = VariableAddress[y] : +# 335| m335_8(int) = InitializeParameter[y] : &:r335_7 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_6 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +# 345| r345_1(glval) = VariableAddress[#return] : +# 345| r345_2(glval) = VariableAddress[ret] : +# 345| r345_3(int) = Load[ret] : &:r345_2 +# 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 +# 335| r335_9(glval) = VariableAddress[#return] : +# 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 +# 335| v335_11(void) = AliasedUse : m335_3 +# 335| v335_12(void) = ExitFunction : + +# 335| Block 2 +# 335| v335_13(void) = Unreached : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| m348_2(unknown) = AliasedDefinition : +# 348| m348_3(unknown) = InitializeNonLocal : +# 348| m348_4(unknown) = Chi : total:m348_2, partial:m348_3 +# 348| r348_5(glval) = VariableAddress[x] : +# 348| m348_6(int) = InitializeParameter[x] : &:r348_5 +# 348| r348_7(glval) = VariableAddress[y] : +# 348| m348_8(int) = InitializeParameter[y] : &:r348_7 +# 348| r348_9(glval) = VariableAddress[z] : +# 348| m348_10(int) = InitializeParameter[z] : &:r348_9 +# 348| r348_11(glval) = VariableAddress[b1] : +# 348| m348_12(bool) = InitializeParameter[b1] : &:r348_11 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_12 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_6 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 4 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_8 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 4 + +# 362| Block 4 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_13(glval) = VariableAddress[#return] : +# 348| v348_14(void) = ReturnValue : &:r348_13, m362_5 +# 348| v348_15(void) = AliasedUse : m348_3 +# 348| v348_16(void) = ExitFunction : + +# 348| Block 5 +# 348| v348_17(void) = Unreached : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| m365_2(unknown) = AliasedDefinition : +# 365| m365_3(unknown) = InitializeNonLocal : +# 365| m365_4(unknown) = Chi : total:m365_2, partial:m365_3 +# 365| r365_5(glval) = VariableAddress[x] : +# 365| m365_6(int) = InitializeParameter[x] : &:r365_5 +# 365| r365_7(glval) = VariableAddress[y] : +# 365| m365_8(int) = InitializeParameter[y] : &:r365_7 +# 365| r365_9(glval) = VariableAddress[b1] : +# 365| m365_10(bool) = InitializeParameter[b1] : &:r365_9 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_6 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_10 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_6 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 4 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_6 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 4 + +# 380| Block 4 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_11(glval) = VariableAddress[#return] : +# 365| v365_12(void) = ReturnValue : &:r365_11, m380_9 +# 365| v365_13(void) = AliasedUse : m365_3 +# 365| v365_14(void) = ExitFunction : + +# 365| Block 5 +# 365| v365_15(void) = Unreached : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| m383_2(unknown) = AliasedDefinition : +# 383| m383_3(unknown) = InitializeNonLocal : +# 383| m383_4(unknown) = Chi : total:m383_2, partial:m383_3 +# 383| r383_5(glval) = VariableAddress[x] : +# 383| m383_6(int) = InitializeParameter[x] : &:r383_5 +# 383| r383_7(glval) = VariableAddress[y] : +# 383| m383_8(int) = InitializeParameter[y] : &:r383_7 +# 383| r383_9(glval) = VariableAddress[z] : +# 383| m383_10(int) = InitializeParameter[z] : &:r383_9 +# 383| r383_11(glval) = VariableAddress[b1] : +# 383| m383_12(bool) = InitializeParameter[b1] : &:r383_11 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_12 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_6 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 4 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_8 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +# 395| v395_1(void) = NoOp : +#-----| Goto -> Block 4 + +# 398| Block 4 +# 398| m398_1(int) = Phi : from 1:m388_4 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_13(glval) = VariableAddress[#return] : +# 383| v383_14(void) = ReturnValue : &:r383_13, m398_5 +# 383| v383_15(void) = AliasedUse : m383_3 +# 383| v383_16(void) = ExitFunction : + +# 383| Block 5 +# 383| v383_17(void) = Unreached : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index a877587ea21..5af11581b0c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1579,3 +1579,228 @@ ssa.cpp: # 319| v319_10(void) = ReturnVoid : # 319| v319_11(void) = AliasedUse : ~m332_10 # 319| v319_12(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| m335_2(unknown) = AliasedDefinition : +# 335| m335_3(unknown) = InitializeNonLocal : +# 335| m335_4(unknown) = Chi : total:m335_2, partial:m335_3 +# 335| r335_5(glval) = VariableAddress[x] : +# 335| m335_6(int) = InitializeParameter[x] : &:r335_5 +# 335| r335_7(glval) = VariableAddress[y] : +# 335| m335_8(int) = InitializeParameter[y] : &:r335_7 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_6 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +# 345| r345_1(glval) = VariableAddress[#return] : +# 345| r345_2(glval) = VariableAddress[ret] : +# 345| r345_3(int) = Load[ret] : &:r345_2 +# 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 +# 335| r335_9(glval) = VariableAddress[#return] : +# 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 +# 335| v335_11(void) = AliasedUse : m335_3 +# 335| v335_12(void) = ExitFunction : + +# 335| Block 2 +# 335| v335_13(void) = Unreached : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| m348_2(unknown) = AliasedDefinition : +# 348| m348_3(unknown) = InitializeNonLocal : +# 348| m348_4(unknown) = Chi : total:m348_2, partial:m348_3 +# 348| r348_5(glval) = VariableAddress[x] : +# 348| m348_6(int) = InitializeParameter[x] : &:r348_5 +# 348| r348_7(glval) = VariableAddress[y] : +# 348| m348_8(int) = InitializeParameter[y] : &:r348_7 +# 348| r348_9(glval) = VariableAddress[z] : +# 348| m348_10(int) = InitializeParameter[z] : &:r348_9 +# 348| r348_11(glval) = VariableAddress[b1] : +# 348| m348_12(bool) = InitializeParameter[b1] : &:r348_11 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_12 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_6 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 4 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_8 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 4 + +# 362| Block 4 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_13(glval) = VariableAddress[#return] : +# 348| v348_14(void) = ReturnValue : &:r348_13, m362_5 +# 348| v348_15(void) = AliasedUse : m348_3 +# 348| v348_16(void) = ExitFunction : + +# 348| Block 5 +# 348| v348_17(void) = Unreached : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| m365_2(unknown) = AliasedDefinition : +# 365| m365_3(unknown) = InitializeNonLocal : +# 365| m365_4(unknown) = Chi : total:m365_2, partial:m365_3 +# 365| r365_5(glval) = VariableAddress[x] : +# 365| m365_6(int) = InitializeParameter[x] : &:r365_5 +# 365| r365_7(glval) = VariableAddress[y] : +# 365| m365_8(int) = InitializeParameter[y] : &:r365_7 +# 365| r365_9(glval) = VariableAddress[b1] : +# 365| m365_10(bool) = InitializeParameter[b1] : &:r365_9 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_6 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_10 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_6 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 4 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_6 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 4 + +# 380| Block 4 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_11(glval) = VariableAddress[#return] : +# 365| v365_12(void) = ReturnValue : &:r365_11, m380_9 +# 365| v365_13(void) = AliasedUse : m365_3 +# 365| v365_14(void) = ExitFunction : + +# 365| Block 5 +# 365| v365_15(void) = Unreached : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| m383_2(unknown) = AliasedDefinition : +# 383| m383_3(unknown) = InitializeNonLocal : +# 383| m383_4(unknown) = Chi : total:m383_2, partial:m383_3 +# 383| r383_5(glval) = VariableAddress[x] : +# 383| m383_6(int) = InitializeParameter[x] : &:r383_5 +# 383| r383_7(glval) = VariableAddress[y] : +# 383| m383_8(int) = InitializeParameter[y] : &:r383_7 +# 383| r383_9(glval) = VariableAddress[z] : +# 383| m383_10(int) = InitializeParameter[z] : &:r383_9 +# 383| r383_11(glval) = VariableAddress[b1] : +# 383| m383_12(bool) = InitializeParameter[b1] : &:r383_11 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_12 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_6 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 4 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_8 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +# 395| v395_1(void) = NoOp : +#-----| Goto -> Block 4 + +# 398| Block 4 +# 398| m398_1(int) = Phi : from 1:m388_4 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_13(glval) = VariableAddress[#return] : +# 383| v383_14(void) = ReturnValue : &:r383_13, m398_5 +# 383| v383_15(void) = AliasedUse : m383_3 +# 383| v383_16(void) = ExitFunction : + +# 383| Block 5 +# 383| v383_17(void) = Unreached : diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index 73aefa17dae..ec8ea81e9e4 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -331,3 +331,69 @@ void DoubleIndirectionEscapes(char *s) sink(ptr2); // $ SPURIOUS: ast sink(*ptr2); // $ ast MISSING: ir } + +int UnreachablePhiOperand(int x, int y) { + bool b = true; + int ret; + + if(b) { + ret = x; + } else { + ret = y; + } + + return ret; +} + +int UnreachablePhiOperand2(int x, int y, int z, bool b1) { + bool b2 = true; + int ret; + + if(b1) { + ret = x; + } else { + if(b2) { + ret = y; + } else { + ret = z; + } + } + + return ret; +} + +int DegeneratePhi(int x, int y, bool b1) { + bool b2 = true; + int ret1; + int ret2 = x; + + if(b1) { + ret1 = x; + } else { + if(b2) { + ret1 = x; + } else { + ret2 = y; + } + } + + return ret1 + ret2; +} + +int FusedBlockPhiOperand(int x, int y, int z, bool b1) { + bool b2 = true; + int ret; + + if(b1) { + ret = x; + } else { + if(b2) { + ret = y; + } else { + ret = z; + } + ; // creates a NoOp instruction with its own basic block + } + + return ret; +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index b396d50c366..9b1ebd7fe6e 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -1450,3 +1450,248 @@ ssa.cpp: # 319| v319_9(void) = ReturnVoid : # 319| v319_10(void) = AliasedUse : ~m? # 319| v319_11(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| mu335_2(unknown) = AliasedDefinition : +# 335| mu335_3(unknown) = InitializeNonLocal : +# 335| r335_4(glval) = VariableAddress[x] : +# 335| m335_5(int) = InitializeParameter[x] : &:r335_4 +# 335| r335_6(glval) = VariableAddress[y] : +# 335| m335_7(int) = InitializeParameter[y] : &:r335_6 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_5 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +#-----| Goto -> Block 3 + +# 342| Block 2 +# 342| r342_1(glval) = VariableAddress[y] : +# 342| r342_2(int) = Load[y] : &:r342_1, m335_7 +# 342| r342_3(glval) = VariableAddress[ret] : +# 342| m342_4(int) = Store[ret] : &:r342_3, r342_2 +#-----| Goto -> Block 3 + +# 345| Block 3 +# 345| m345_1(int) = Phi : from 1:m340_4, from 2:m342_4 +# 345| r345_2(glval) = VariableAddress[#return] : +# 345| r345_3(glval) = VariableAddress[ret] : +# 345| r345_4(int) = Load[ret] : &:r345_3, m345_1 +# 345| m345_5(int) = Store[#return] : &:r345_2, r345_4 +# 335| r335_8(glval) = VariableAddress[#return] : +# 335| v335_9(void) = ReturnValue : &:r335_8, m345_5 +# 335| v335_10(void) = AliasedUse : ~m? +# 335| v335_11(void) = ExitFunction : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| mu348_2(unknown) = AliasedDefinition : +# 348| mu348_3(unknown) = InitializeNonLocal : +# 348| r348_4(glval) = VariableAddress[x] : +# 348| m348_5(int) = InitializeParameter[x] : &:r348_4 +# 348| r348_6(glval) = VariableAddress[y] : +# 348| m348_7(int) = InitializeParameter[y] : &:r348_6 +# 348| r348_8(glval) = VariableAddress[z] : +# 348| m348_9(int) = InitializeParameter[z] : &:r348_8 +# 348| r348_10(glval) = VariableAddress[b1] : +# 348| m348_11(bool) = InitializeParameter[b1] : &:r348_10 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_11 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_5 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 5 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_7 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 5 + +# 358| Block 4 +# 358| r358_1(glval) = VariableAddress[z] : +# 358| r358_2(int) = Load[z] : &:r358_1, m348_9 +# 358| r358_3(glval) = VariableAddress[ret] : +# 358| m358_4(int) = Store[ret] : &:r358_3, r358_2 +#-----| Goto -> Block 5 + +# 362| Block 5 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4, from 4:m358_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_12(glval) = VariableAddress[#return] : +# 348| v348_13(void) = ReturnValue : &:r348_12, m362_5 +# 348| v348_14(void) = AliasedUse : ~m? +# 348| v348_15(void) = ExitFunction : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| mu365_2(unknown) = AliasedDefinition : +# 365| mu365_3(unknown) = InitializeNonLocal : +# 365| r365_4(glval) = VariableAddress[x] : +# 365| m365_5(int) = InitializeParameter[x] : &:r365_4 +# 365| r365_6(glval) = VariableAddress[y] : +# 365| m365_7(int) = InitializeParameter[y] : &:r365_6 +# 365| r365_8(glval) = VariableAddress[b1] : +# 365| m365_9(bool) = InitializeParameter[b1] : &:r365_8 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_5 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_9 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_5 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 5 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_5 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 5 + +# 376| Block 4 +# 376| r376_1(glval) = VariableAddress[y] : +# 376| r376_2(int) = Load[y] : &:r376_1, m365_7 +# 376| r376_3(glval) = VariableAddress[ret2] : +# 376| m376_4(int) = Store[ret2] : &:r376_3, r376_2 +#-----| Goto -> Block 5 + +# 380| Block 5 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4, from 4:m376_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4, from 4:m367_2 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_10(glval) = VariableAddress[#return] : +# 365| v365_11(void) = ReturnValue : &:r365_10, m380_9 +# 365| v365_12(void) = AliasedUse : ~m? +# 365| v365_13(void) = ExitFunction : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| mu383_2(unknown) = AliasedDefinition : +# 383| mu383_3(unknown) = InitializeNonLocal : +# 383| r383_4(glval) = VariableAddress[x] : +# 383| m383_5(int) = InitializeParameter[x] : &:r383_4 +# 383| r383_6(glval) = VariableAddress[y] : +# 383| m383_7(int) = InitializeParameter[y] : &:r383_6 +# 383| r383_8(glval) = VariableAddress[z] : +# 383| m383_9(int) = InitializeParameter[z] : &:r383_8 +# 383| r383_10(glval) = VariableAddress[b1] : +# 383| m383_11(bool) = InitializeParameter[b1] : &:r383_10 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_11 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_5 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 6 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_7 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +#-----| Goto -> Block 5 + +# 393| Block 4 +# 393| r393_1(glval) = VariableAddress[z] : +# 393| r393_2(int) = Load[z] : &:r393_1, m383_9 +# 393| r393_3(glval) = VariableAddress[ret] : +# 393| m393_4(int) = Store[ret] : &:r393_3, r393_2 +#-----| Goto -> Block 5 + +# 395| Block 5 +# 395| m395_1(int) = Phi : from 3:m391_4, from 4:m393_4 +# 395| v395_2(void) = NoOp : +#-----| Goto -> Block 6 + +# 398| Block 6 +# 398| m398_1(int) = Phi : from 1:m388_4, from 5:m395_1 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_12(glval) = VariableAddress[#return] : +# 383| v383_13(void) = ReturnValue : &:r383_12, m398_5 +# 383| v383_14(void) = AliasedUse : ~m? +# 383| v383_15(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index b396d50c366..9b1ebd7fe6e 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1450,3 +1450,248 @@ ssa.cpp: # 319| v319_9(void) = ReturnVoid : # 319| v319_10(void) = AliasedUse : ~m? # 319| v319_11(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| mu335_2(unknown) = AliasedDefinition : +# 335| mu335_3(unknown) = InitializeNonLocal : +# 335| r335_4(glval) = VariableAddress[x] : +# 335| m335_5(int) = InitializeParameter[x] : &:r335_4 +# 335| r335_6(glval) = VariableAddress[y] : +# 335| m335_7(int) = InitializeParameter[y] : &:r335_6 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_5 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +#-----| Goto -> Block 3 + +# 342| Block 2 +# 342| r342_1(glval) = VariableAddress[y] : +# 342| r342_2(int) = Load[y] : &:r342_1, m335_7 +# 342| r342_3(glval) = VariableAddress[ret] : +# 342| m342_4(int) = Store[ret] : &:r342_3, r342_2 +#-----| Goto -> Block 3 + +# 345| Block 3 +# 345| m345_1(int) = Phi : from 1:m340_4, from 2:m342_4 +# 345| r345_2(glval) = VariableAddress[#return] : +# 345| r345_3(glval) = VariableAddress[ret] : +# 345| r345_4(int) = Load[ret] : &:r345_3, m345_1 +# 345| m345_5(int) = Store[#return] : &:r345_2, r345_4 +# 335| r335_8(glval) = VariableAddress[#return] : +# 335| v335_9(void) = ReturnValue : &:r335_8, m345_5 +# 335| v335_10(void) = AliasedUse : ~m? +# 335| v335_11(void) = ExitFunction : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| mu348_2(unknown) = AliasedDefinition : +# 348| mu348_3(unknown) = InitializeNonLocal : +# 348| r348_4(glval) = VariableAddress[x] : +# 348| m348_5(int) = InitializeParameter[x] : &:r348_4 +# 348| r348_6(glval) = VariableAddress[y] : +# 348| m348_7(int) = InitializeParameter[y] : &:r348_6 +# 348| r348_8(glval) = VariableAddress[z] : +# 348| m348_9(int) = InitializeParameter[z] : &:r348_8 +# 348| r348_10(glval) = VariableAddress[b1] : +# 348| m348_11(bool) = InitializeParameter[b1] : &:r348_10 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_11 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_5 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 5 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_7 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 5 + +# 358| Block 4 +# 358| r358_1(glval) = VariableAddress[z] : +# 358| r358_2(int) = Load[z] : &:r358_1, m348_9 +# 358| r358_3(glval) = VariableAddress[ret] : +# 358| m358_4(int) = Store[ret] : &:r358_3, r358_2 +#-----| Goto -> Block 5 + +# 362| Block 5 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4, from 4:m358_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_12(glval) = VariableAddress[#return] : +# 348| v348_13(void) = ReturnValue : &:r348_12, m362_5 +# 348| v348_14(void) = AliasedUse : ~m? +# 348| v348_15(void) = ExitFunction : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| mu365_2(unknown) = AliasedDefinition : +# 365| mu365_3(unknown) = InitializeNonLocal : +# 365| r365_4(glval) = VariableAddress[x] : +# 365| m365_5(int) = InitializeParameter[x] : &:r365_4 +# 365| r365_6(glval) = VariableAddress[y] : +# 365| m365_7(int) = InitializeParameter[y] : &:r365_6 +# 365| r365_8(glval) = VariableAddress[b1] : +# 365| m365_9(bool) = InitializeParameter[b1] : &:r365_8 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_5 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_9 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_5 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 5 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_5 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 5 + +# 376| Block 4 +# 376| r376_1(glval) = VariableAddress[y] : +# 376| r376_2(int) = Load[y] : &:r376_1, m365_7 +# 376| r376_3(glval) = VariableAddress[ret2] : +# 376| m376_4(int) = Store[ret2] : &:r376_3, r376_2 +#-----| Goto -> Block 5 + +# 380| Block 5 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4, from 4:m376_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4, from 4:m367_2 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_10(glval) = VariableAddress[#return] : +# 365| v365_11(void) = ReturnValue : &:r365_10, m380_9 +# 365| v365_12(void) = AliasedUse : ~m? +# 365| v365_13(void) = ExitFunction : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| mu383_2(unknown) = AliasedDefinition : +# 383| mu383_3(unknown) = InitializeNonLocal : +# 383| r383_4(glval) = VariableAddress[x] : +# 383| m383_5(int) = InitializeParameter[x] : &:r383_4 +# 383| r383_6(glval) = VariableAddress[y] : +# 383| m383_7(int) = InitializeParameter[y] : &:r383_6 +# 383| r383_8(glval) = VariableAddress[z] : +# 383| m383_9(int) = InitializeParameter[z] : &:r383_8 +# 383| r383_10(glval) = VariableAddress[b1] : +# 383| m383_11(bool) = InitializeParameter[b1] : &:r383_10 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_11 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_5 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 6 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_7 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +#-----| Goto -> Block 5 + +# 393| Block 4 +# 393| r393_1(glval) = VariableAddress[z] : +# 393| r393_2(int) = Load[z] : &:r393_1, m383_9 +# 393| r393_3(glval) = VariableAddress[ret] : +# 393| m393_4(int) = Store[ret] : &:r393_3, r393_2 +#-----| Goto -> Block 5 + +# 395| Block 5 +# 395| m395_1(int) = Phi : from 3:m391_4, from 4:m393_4 +# 395| v395_2(void) = NoOp : +#-----| Goto -> Block 6 + +# 398| Block 6 +# 398| m398_1(int) = Phi : from 1:m388_4, from 5:m395_1 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_12(glval) = VariableAddress[#return] : +# 383| v383_13(void) = ReturnValue : &:r383_12, m398_5 +# 383| v383_14(void) = AliasedUse : ~m? +# 383| v383_15(void) = ExitFunction : From 6600436dd930151f0a1caf24d9b0e187e1faff4c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 19 Apr 2021 17:21:34 -0700 Subject: [PATCH 230/550] C++: handle degenerate phi nodes --- .../aliased_ssa/internal/SSAConstruction.qll | 31 +++++++++++++------ .../ir/ssa/aliased_ssa_consistency.expected | 4 --- .../aliased_ssa_consistency_unsound.expected | 4 --- .../ir/ssa/aliased_ssa_ir.expected | 2 +- .../ir/ssa/aliased_ssa_ir_unsound.expected | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 343499dcc6c..34a2e031f0f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -43,11 +43,25 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr instanceof TPhiInstruction and + not exists(getDegeneratePhiOperand(instr)) or instr instanceof TChiInstruction or @@ -150,16 +164,13 @@ private module Cached { ( result = getNewInstruction(oldOperand.getAnyDef()) and overlap = originalOverlap - /* - * or - * exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | - * phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and - * result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - * overlap = combineOverlap(phiOperandOverlap, originalOverlap) - * ) - */ - + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = combineOverlap(phiOperandOverlap, originalOverlap) ) + ) ) } diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected index f0de9cc90d5..e358bfd49da 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected @@ -10,13 +10,9 @@ instructionWithoutSuccessor ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction -| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | -| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | memoryOperandDefinitionIsUnmodeled operandAcrossFunctions instructionWithoutUniqueBlock -| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is a member of 0 blocks in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | -| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is a member of 0 blocks in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | containsLoopOfForwardEdges lostReachability backEdgeCountMismatch diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected index f0de9cc90d5..e358bfd49da 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected @@ -10,13 +10,9 @@ instructionWithoutSuccessor ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction -| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | -| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is in a block with only 0 predecessors in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | memoryOperandDefinitionIsUnmodeled operandAcrossFunctions instructionWithoutUniqueBlock -| ssa.cpp:345:3:345:13 | Phi: return ... | Instruction 'Phi: return ...' is a member of 0 blocks in function '$@'. | ssa.cpp:335:5:335:25 | int UnreachablePhiOperand(int, int) | int UnreachablePhiOperand(int, int) | -| ssa.cpp:395:5:395:5 | Phi: ; | Instruction 'Phi: ;' is a member of 0 blocks in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | containsLoopOfForwardEdges lostReachability backEdgeCountMismatch diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index d53c52e058c..6a7cb2cfa1d 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1618,7 +1618,7 @@ ssa.cpp: # 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 # 345| r345_1(glval) = VariableAddress[#return] : # 345| r345_2(glval) = VariableAddress[ret] : -# 345| r345_3(int) = Load[ret] : &:r345_2 +# 345| r345_3(int) = Load[ret] : &:r345_2, m340_4 # 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 # 335| r335_9(glval) = VariableAddress[#return] : # 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index 5af11581b0c..8ca46003e1b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1608,7 +1608,7 @@ ssa.cpp: # 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 # 345| r345_1(glval) = VariableAddress[#return] : # 345| r345_2(glval) = VariableAddress[ret] : -# 345| r345_3(int) = Load[ret] : &:r345_2 +# 345| r345_3(int) = Load[ret] : &:r345_2, m340_4 # 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 # 335| r335_9(glval) = VariableAddress[#return] : # 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 From 195b8114222ae91e2608165dc981a343e16471c2 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 20 Apr 2021 16:47:20 -0700 Subject: [PATCH 231/550] C++: handle phi operands from unreachable blocks --- .../aliased_ssa/internal/SSAConstruction.qll | 20 +++++++++++++++--- .../ir/implementation/internal/TOperand.qll | 21 ++++++++++--------- .../ir/ssa/aliased_ssa_consistency.expected | 1 - .../aliased_ssa_consistency_unsound.expected | 1 - .../ir/ssa/aliased_ssa_ir.expected | 2 +- .../ir/ssa/aliased_ssa_ir_unsound.expected | 2 +- 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 34a2e031f0f..e12faff0c84 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -68,8 +68,22 @@ private module Cached { instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) } /** @@ -164,7 +178,7 @@ private module Cached { ( result = getNewInstruction(oldOperand.getAnyDef()) and overlap = originalOverlap - or + or exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll index 57cbb995a00..e86494af03a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll @@ -36,10 +36,9 @@ private module Internal { useInstr.getOpcode().hasOperand(tag) } or TUnaliasedPhiOperand( - Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, - Unaliased::IRBlock predecessorBlock, Overlap overlap + Unaliased::PhiInstruction useInstr, Unaliased::IRBlock predecessorBlock, Overlap overlap ) { - defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + exists(UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)) } or //// ALIASED //// @@ -50,10 +49,9 @@ private module Internal { // important that we use the same definition of "is variable aliased" across // the phases. TAliasedPhiOperand( - TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr, - Aliased::IRBlock predecessorBlock, Overlap overlap + TAliasedSSAPhiInstruction useInstr, Aliased::IRBlock predecessorBlock, Overlap overlap ) { - defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + exists(AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)) } or TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() } } @@ -144,7 +142,8 @@ module UnaliasedSSAOperands { Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, Unaliased::IRBlock predecessorBlock, Overlap overlap ) { - result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and + result = Internal::TUnaliasedPhiOperand(useInstr, predecessorBlock, overlap) } TPhiOperand reusedPhiOperand( @@ -182,7 +181,8 @@ module AliasedSSAOperands { Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr, Aliased::IRBlock predecessorBlock, Overlap overlap ) { - result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and + result = Internal::TAliasedPhiOperand(useInstr, predecessorBlock, overlap) } /** @@ -193,8 +193,9 @@ module AliasedSSAOperands { Aliased::IRBlock predecessorBlock, Overlap overlap ) { exists(Unaliased::IRBlock oldBlock | - predecessorBlock.getFirstInstruction() = oldBlock.getFirstInstruction() and - result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, oldBlock, overlap) + predecessorBlock = AliasedConstruction::getNewBlock(oldBlock) and + result = Internal::TUnaliasedPhiOperand(useInstr, oldBlock, _) and + defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) ) } diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected index e358bfd49da..31e5b01229c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected @@ -2,7 +2,6 @@ missingOperand unexpectedOperand duplicateOperand missingPhiOperand -| ssa.cpp:398:3:398:13 | Phi: return ... | Instruction 'Phi: return ...' is missing an operand for predecessor block 'VariableAddress: y' in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | missingOperandType duplicateChiOperand sideEffectWithoutPrimary diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected index e358bfd49da..31e5b01229c 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected @@ -2,7 +2,6 @@ missingOperand unexpectedOperand duplicateOperand missingPhiOperand -| ssa.cpp:398:3:398:13 | Phi: return ... | Instruction 'Phi: return ...' is missing an operand for predecessor block 'VariableAddress: y' in function '$@'. | ssa.cpp:383:5:383:24 | int FusedBlockPhiOperand(int, int, int, bool) | int FusedBlockPhiOperand(int, int, int, bool) | missingOperandType duplicateChiOperand sideEffectWithoutPrimary diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 6a7cb2cfa1d..59505ce6f39 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1802,7 +1802,7 @@ ssa.cpp: #-----| Goto -> Block 4 # 398| Block 4 -# 398| m398_1(int) = Phi : from 1:m388_4 +# 398| m398_1(int) = Phi : from 1:m388_4, from 3:m391_4 # 398| r398_2(glval) = VariableAddress[#return] : # 398| r398_3(glval) = VariableAddress[ret] : # 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index 8ca46003e1b..b87b65363dc 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1792,7 +1792,7 @@ ssa.cpp: #-----| Goto -> Block 4 # 398| Block 4 -# 398| m398_1(int) = Phi : from 1:m388_4 +# 398| m398_1(int) = Phi : from 1:m388_4, from 3:m391_4 # 398| r398_2(glval) = VariableAddress[#return] : # 398| r398_3(glval) = VariableAddress[ret] : # 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 From 1f69b312393c96a3f735374b620a8d8c17c99342 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 20 Apr 2021 16:52:19 -0700 Subject: [PATCH 232/550] C++: test changes in annotate_sinks_only --- .../annotate_sinks_only/defaulttainttracking.cpp | 4 ++-- .../DefaultTaintTracking/annotate_sinks_only/tainted.expected | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp index d64f1966b49..843587b576a 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp @@ -187,10 +187,10 @@ void test_pointers1() ptr4 = &ptr3; sink(buffer); // $ ast,ir - sink(ptr1); // $ ast,ir + sink(ptr1); // $ ast MISSING: ir sink(ptr2); // $ SPURIOUS: ast sink(*ptr2); // $ ast MISSING: ir - sink(ptr3); // $ ast,ir + sink(ptr3); // $ ast MISSING: ir sink(ptr4); // $ SPURIOUS: ast sink(*ptr4); // $ ast MISSING: ir } diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected index 8babe64dfc1..e69de29bb2d 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/tainted.expected @@ -1,2 +0,0 @@ -| defaulttainttracking.cpp:190:14:190:24 | // $ ast,ir | Missing result:ir= | -| defaulttainttracking.cpp:193:14:193:24 | // $ ast,ir | Missing result:ir= | From 5d7d26bed12f76fa4f3b8f31aaba25e0af36e11d Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 21 Apr 2021 16:17:50 -0700 Subject: [PATCH 233/550] C++: fixups and file sync for SSA sharing --- .../aliased_ssa/internal/AliasAnalysis.qll | 4 +- .../internal/AliasConfiguration.qll | 6 +- .../aliased_ssa/internal/AliasedSSA.qll | 2 +- .../aliased_ssa/internal/SSAConstruction.qll | 15 ++++- .../unaliased_ssa/internal/AliasAnalysis.qll | 4 +- .../internal/AliasConfiguration.qll | 6 +- .../internal/SSAConstruction.qll | 60 +++++++++++++++---- 7 files changed, 66 insertions(+), 31 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 1198c828ecb..1082b0d6a48 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -329,9 +329,7 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - exists(Configuration::StageEscapeConfiguration config | - config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) - ) + Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index 236db922646..8ba91d70087 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -143,8 +143,4 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { final override predicate alwaysEscapes() { none() } } -class StageEscapeConfiguration extends string { - StageEscapeConfiguration() { this = "StageEscapeConfiguration (aliased_ssa)" } - - predicate useSoundEscapeAnalysis() { none() } -} +predicate phaseNeedsSoundEscapeAnalysis() { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index 74638b6ec1d..acdae2b758a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -133,7 +133,7 @@ abstract class MemoryLocation extends TMemoryLocation { */ predicate isAlwaysAllocatedOnStack() { none() } - final predicate canReuseSSA() { any() } + final predicate canReuseSSA() { none() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index e12faff0c84..5192d8de9da 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -60,8 +60,19 @@ private module Cached { predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction and - not exists(getDegeneratePhiOperand(instr)) + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + oldInstruction = instr and + operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand1.getPredecessorBlock() instanceof OldBlock and + operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand2.getPredecessorBlock() instanceof OldBlock and + operand1 != operand2 + ) or instr instanceof TChiInstruction or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1198c828ecb..1082b0d6a48 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -329,9 +329,7 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - exists(Configuration::StageEscapeConfiguration config | - config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) - ) + Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index d39288cd412..dbdd3c14c85 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -15,8 +15,4 @@ class Allocation extends IRAutomaticVariable { } } -class StageEscapeConfiguration extends string { - StageEscapeConfiguration() { this = "StageEscapeConfiguration (unaliased_ssa)" } - - predicate useSoundEscapeAnalysis() { any() } -} +predicate phaseNeedsSoundEscapeAnalysis() { any() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 343499dcc6c..5192d8de9da 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -43,19 +43,58 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + oldInstruction = instr and + operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand1.getPredecessorBlock() instanceof OldBlock and + operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand2.getPredecessorBlock() instanceof OldBlock and + operand1 != operand2 + ) or instr instanceof TChiInstruction or instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) } /** @@ -150,16 +189,13 @@ private module Cached { ( result = getNewInstruction(oldOperand.getAnyDef()) and overlap = originalOverlap - /* - * or - * exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | - * phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and - * result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - * overlap = combineOverlap(phiOperandOverlap, originalOverlap) - * ) - */ - + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = combineOverlap(phiOperandOverlap, originalOverlap) ) + ) ) } From b2811022d7126fc18011d9f1de65c4f7d99dfbb3 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 22 Apr 2021 14:10:54 -0700 Subject: [PATCH 234/550] C#: sync IR files and update for C++ SSA reuse --- .../implementation/internal/TInstruction.qll | 8 +- .../ir/implementation/internal/TOperand.qll | 20 +++ .../ir/implementation/raw/Instruction.qll | 8 ++ .../ir/implementation/raw/Operand.qll | 18 ++- .../unaliased_ssa/Instruction.qll | 8 ++ .../implementation/unaliased_ssa/Operand.qll | 18 ++- .../unaliased_ssa/internal/AliasAnalysis.qll | 8 +- .../internal/AliasConfiguration.qll | 8 +- .../internal/SSAConstruction.qll | 124 +++++++++++++++++- .../unaliased_ssa/internal/SimpleSSA.qll | 4 +- 10 files changed, 186 insertions(+), 38 deletions(-) diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll index 911d9af78fa..4b3f19cbdde 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll @@ -55,10 +55,7 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } - TPhiInstruction reusedPhiInstruction( - TRawInstruction blockStartInstr) { - none() - } + TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() } class TChiInstruction = TUnaliasedSSAChiInstruction; @@ -88,8 +85,7 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } - TPhiInstruction reusedPhiInstruction( - TRawInstruction blockStartInstr) { + TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) } diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll b/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll index 9c3e7620186..20fe77f8223 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll @@ -92,6 +92,16 @@ module RawOperands { none() } + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock, + Overlap overlap + ) { + none() + } + /** * Returns the Chi operand with the specified parameters. */ @@ -123,6 +133,16 @@ module UnaliasedSSAOperands { result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) } + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, + Unaliased::IRBlock predecessorBlock, Overlap overlap + ) { + none() + } + /** * Returns the Chi operand with the specified parameters. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index e335080ad6b..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index e335080ad6b..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 83492e6ab79..1082b0d6a48 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -92,9 +92,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType or instr instanceof CallInstruction and - not exists(IREscapeAnalysisConfiguration config | - config.useSoundEscapeAnalysis() - ) + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or @@ -331,9 +329,7 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - exists(Configuration::StageEscapeConfiguration config | - config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) - ) + Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 2ca333ad25c..dbdd3c14c85 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -15,10 +15,4 @@ class Allocation extends IRAutomaticVariable { } } -class StageEscapeConfiguration extends string { - StageEscapeConfiguration() { - this = "StageEscapeConfiguration (unaliased_ssa)" - } - - predicate useSoundEscapeAnalysis() { any() } -} +predicate phaseNeedsSoundEscapeAnalysis() { any() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 262865fa1d1..5192d8de9da 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -43,24 +43,82 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + oldInstruction = instr and + operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand1.getPredecessorBlock() instanceof OldBlock and + operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand2.getPredecessorBlock() instanceof OldBlock and + operand1 != operand2 + ) or instr instanceof TChiInstruction or instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) + } + + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) } cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +175,30 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = combineOverlap(phiOperandOverlap, originalOverlap) + ) + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +230,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +302,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +331,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -249,7 +353,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,6 +444,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 906699b6394..f3e02c9f6a8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -72,9 +72,7 @@ class MemoryLocation extends TMemoryLocation { final predicate canReuseSSA() { canReuseSSAForVariable(var) } } -predicate canReuseSSAForOldResult(Instruction instr) { - none() -} +predicate canReuseSSAForOldResult(Instruction instr) { none() } /** * Represents a set of `MemoryLocation`s that cannot overlap with From 230f4bcae83aca11e241ce07b2859a581c953a73 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 27 Apr 2021 14:32:14 -0700 Subject: [PATCH 235/550] C++: accept test changes from IR sharing --- .../Security/CWE/CWE-134/semmle/ifs/ifs.c | 4 +-- .../CWE/CWE-134/semmle/ifs/ifs.expected | 36 ------------------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c index 3d15905d82d..69a46dd3879 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c @@ -86,13 +86,13 @@ int main(int argc, char **argv) { i3 = argv[1]; printf(i3); - // BAD: varOne is 1 so condition is true and it always goes inside the if + // BAD [FALSE NEGATIVE]: varOne is 1 so condition is true and it always goes inside the if char *i4; if (varOne) i4 = argv[1]; printf(i4); - // BAD: varZero is 0 so condition is true and it always goes inside the if + // BAD [FALSE NEGATIVE]: varZero is 0 so condition is true and it always goes inside the if char *i5; if (!varZero) i5 = argv[1]; diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected index 50ae940400a..8c2c89035e2 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected @@ -39,22 +39,6 @@ edges | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 | | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection | | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 | @@ -133,24 +117,6 @@ nodes | ifs.c:87:9:87:10 | i3 | semmle.label | i3 | | ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection | | ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection | -| ifs.c:92:8:92:11 | argv | semmle.label | argv | -| ifs.c:92:8:92:11 | argv | semmle.label | argv | -| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection | -| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection | -| ifs.c:98:8:98:11 | argv | semmle.label | argv | -| ifs.c:98:8:98:11 | argv | semmle.label | argv | -| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection | -| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection | | ifs.c:105:8:105:11 | argv | semmle.label | argv | | ifs.c:105:8:105:11 | argv | semmle.label | argv | | ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... | @@ -193,8 +159,6 @@ nodes | ifs.c:75:9:75:10 | i1 | ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:74:8:74:11 | argv | argv | | ifs.c:81:9:81:10 | i2 | ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:80:8:80:11 | argv | argv | | ifs.c:87:9:87:10 | i3 | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:86:8:86:11 | argv | argv | -| ifs.c:93:9:93:10 | i4 | ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:92:8:92:11 | argv | argv | -| ifs.c:99:9:99:10 | i5 | ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:98:8:98:11 | argv | argv | | ifs.c:106:9:106:10 | i6 | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:105:8:105:11 | argv | argv | | ifs.c:112:9:112:10 | i7 | ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:111:8:111:11 | argv | argv | | ifs.c:118:9:118:10 | i8 | ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:117:8:117:11 | argv | argv | From 5406783e9c49e7f45e5c8b2c12fe531b9c3c869d Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 27 Apr 2021 14:32:22 -0700 Subject: [PATCH 236/550] C++: autoformat --- .../aliased_ssa/internal/AliasAnalysis.qll | 3 ++- .../aliased_ssa/internal/SSAConstruction.qll | 16 ++++++++++------ .../unaliased_ssa/internal/AliasAnalysis.qll | 3 ++- .../unaliased_ssa/internal/SSAConstruction.qll | 16 ++++++++++------ .../unaliased_ssa/internal/AliasAnalysis.qll | 3 ++- .../unaliased_ssa/internal/SSAConstruction.qll | 16 ++++++++++------ 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 1082b0d6a48..26f8a89a235 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -329,7 +329,8 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 5192d8de9da..9c4a0bb0bf5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -65,7 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + exists( + OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, + OldInstruction oldInstruction + | oldInstruction = instr and operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and operand1.getPredecessorBlock() instanceof OldBlock and @@ -79,13 +82,14 @@ private module Cached { instr instanceof TUnreachedInstruction } - cached IRBlock getNewBlock(OldBlock oldBlock) { + cached + IRBlock getNewBlock(OldBlock oldBlock) { exists(Instruction newEnd, OldIR::Instruction oldEnd | ( - result.getLastInstruction() = newEnd and - not newEnd instanceof ChiInstruction - or - newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? ) and ( oldBlock.getLastInstruction() = oldEnd and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1082b0d6a48..26f8a89a235 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -329,7 +329,8 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 5192d8de9da..9c4a0bb0bf5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,7 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + exists( + OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, + OldInstruction oldInstruction + | oldInstruction = instr and operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and operand1.getPredecessorBlock() instanceof OldBlock and @@ -79,13 +82,14 @@ private module Cached { instr instanceof TUnreachedInstruction } - cached IRBlock getNewBlock(OldBlock oldBlock) { + cached + IRBlock getNewBlock(OldBlock oldBlock) { exists(Instruction newEnd, OldIR::Instruction oldEnd | ( - result.getLastInstruction() = newEnd and - not newEnd instanceof ChiInstruction - or - newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? ) and ( oldBlock.getLastInstruction() = oldEnd and diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 1082b0d6a48..26f8a89a235 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -329,7 +329,8 @@ predicate allocationEscapes(Configuration::Allocation allocation) { config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) or - Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 5192d8de9da..9c4a0bb0bf5 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,7 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction | + exists( + OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, + OldInstruction oldInstruction + | oldInstruction = instr and operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and operand1.getPredecessorBlock() instanceof OldBlock and @@ -79,13 +82,14 @@ private module Cached { instr instanceof TUnreachedInstruction } - cached IRBlock getNewBlock(OldBlock oldBlock) { + cached + IRBlock getNewBlock(OldBlock oldBlock) { exists(Instruction newEnd, OldIR::Instruction oldEnd | ( - result.getLastInstruction() = newEnd and - not newEnd instanceof ChiInstruction - or - newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? ) and ( oldBlock.getLastInstruction() = oldEnd and From 35594eac22953e2bcb8de1bc81405f2962f26c5a Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 3 May 2021 11:49:12 -0700 Subject: [PATCH 237/550] C++: fix bad join order in phi node sharing --- .../aliased_ssa/internal/SSAConstruction.qll | 15 ++++----------- .../unaliased_ssa/internal/SSAConstruction.qll | 15 ++++----------- .../unaliased_ssa/internal/SSAConstruction.qll | 15 ++++----------- 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 9c4a0bb0bf5..11c5af43719 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -65,17 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists( - OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, - OldInstruction oldInstruction - | - oldInstruction = instr and - operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand1.getPredecessorBlock() instanceof OldBlock and - operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand2.getPredecessorBlock() instanceof OldBlock and - operand1 != operand2 - ) + not exists(unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + )) or instr instanceof TChiInstruction or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 9c4a0bb0bf5..11c5af43719 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,17 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists( - OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, - OldInstruction oldInstruction - | - oldInstruction = instr and - operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand1.getPredecessorBlock() instanceof OldBlock and - operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand2.getPredecessorBlock() instanceof OldBlock and - operand1 != operand2 - ) + not exists(unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + )) or instr instanceof TChiInstruction or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 9c4a0bb0bf5..11c5af43719 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,17 +65,10 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - exists( - OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, - OldInstruction oldInstruction - | - oldInstruction = instr and - operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand1.getPredecessorBlock() instanceof OldBlock and - operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and - operand2.getPredecessorBlock() instanceof OldBlock and - operand1 != operand2 - ) + not exists(unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + )) or instr instanceof TChiInstruction or From 5318aa8ead6ec2eb487f72af5aeaac2b4f1fd3ce Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 3 May 2021 14:08:35 -0700 Subject: [PATCH 238/550] C++: autoformat --- .../aliased_ssa/internal/SSAConstruction.qll | 10 ++++++---- .../unaliased_ssa/internal/SSAConstruction.qll | 10 ++++++---- .../unaliased_ssa/internal/SSAConstruction.qll | 10 ++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 11c5af43719..5c789276318 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -65,10 +65,12 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - not exists(unique(OldIR::PhiInputOperand operand | - operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and - operand.getPredecessorBlock() instanceof OldBlock - )) + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 11c5af43719..5c789276318 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,10 +65,12 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - not exists(unique(OldIR::PhiInputOperand operand | - operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and - operand.getPredecessorBlock() instanceof OldBlock - )) + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 11c5af43719..5c789276318 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -65,10 +65,12 @@ private module Cached { instr = reusedPhiInstruction(_) and // Check that the phi instruction is *not* degenerate, but we can't use // getDegeneratePhiOperand in the first stage with phi instyructions - not exists(unique(OldIR::PhiInputOperand operand | - operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and - operand.getPredecessorBlock() instanceof OldBlock - )) + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or From b3e598c1a7460b66dfd72f6da0a7c17dab4b5fef Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 4 May 2021 11:34:24 -0700 Subject: [PATCH 239/550] C++/C#: fix another join order in SSA construction --- .../implementation/aliased_ssa/internal/SSAConstruction.qll | 4 +++- .../implementation/unaliased_ssa/internal/SSAConstruction.qll | 4 +++- .../implementation/unaliased_ssa/internal/SSAConstruction.qll | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 5c789276318..63253ce7cda 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -192,7 +192,9 @@ private module Cached { exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - overlap = combineOverlap(phiOperandOverlap, originalOverlap) + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) ) ) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 5c789276318..63253ce7cda 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -192,7 +192,9 @@ private module Cached { exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - overlap = combineOverlap(phiOperandOverlap, originalOverlap) + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) ) ) ) diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 5c789276318..63253ce7cda 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -192,7 +192,9 @@ private module Cached { exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and - overlap = combineOverlap(phiOperandOverlap, originalOverlap) + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) ) ) ) From 5437bd7a41f06e4082906606a31f041527b8e802 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 17:57:57 +0200 Subject: [PATCH 240/550] C++: Fix annotation. --- .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index dde820d5df6..da990934515 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -209,6 +209,6 @@ void bad_new_with_nonthrowing_call() { void bad_new_catch_baseclass_of_bad_alloc() { try { - int* p = new(std::nothrow) int; // BAD [NOT DETECTED] + int* p = new(std::nothrow) int; // BAD } catch(const std::exception&) { } } From 7c1720a1d194e4b4a56547dafbb2b2172f6b0066 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 18:02:05 +0200 Subject: [PATCH 241/550] C++: Remove NoThrowAllocator and inline its (corrected) definition in ThrowingAllocator. --- .../CWE-570/IncorrectAllocationErrorHandling.ql | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 6bb0182a79e..3ca8a5eee54 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -147,19 +147,14 @@ predicate exprMayThrow(Expr e) { ) } -/** An allocator that will not throw an exception. */ -class NoThrowAllocator extends Function { - NoThrowAllocator() { - exists(NewOrNewArrayExpr newExpr | - newExpr.getAllocator() = this and - not functionMayThrow(this) - ) - } -} - /** An allocator that might throw an exception. */ class ThrowingAllocator extends Function { - ThrowingAllocator() { not this instanceof NoThrowAllocator } + ThrowingAllocator() { + exists(NewOrNewArrayExpr newExpr | + newExpr.getAllocator() = this and + functionMayThrow(this) + ) + } } /** The `std::bad_alloc` exception and its `bsl` variant. */ From 856d512aa677a8f3097320918d4f21d7da003128 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 6 May 2021 18:36:09 +0200 Subject: [PATCH 242/550] C++: Simplify noThrowInTryBlock. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 3ca8a5eee54..97ff9e06d64 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -182,9 +182,7 @@ class BadAllocCatchBlock extends CatchBlock { */ predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) { exists(TryStmt try | - forall(Expr cand | cand.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() | - not convertedExprMayThrow(cand) - ) and + not stmtMayThrow(try.getStmt()) and try.getACatchClause() = catchBlock and newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() ) From 852134023da7a697866fab5022564914e816e0de Mon Sep 17 00:00:00 2001 From: alexet Date: Thu, 6 May 2021 18:11:28 +0100 Subject: [PATCH 243/550] Use only bind-out to fix join order. --- csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index cca858d69ba..f06fbca375d 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -387,7 +387,8 @@ private module Internal { result = this.getAStaticTarget() or result.getUnboundDeclaration() = - this.getASubsumedStaticTarget0(Gvn::getGlobalValueNumber(result.getDeclaringType())) + this.getASubsumedStaticTarget0(pragma[only_bind_out](Gvn::getGlobalValueNumber(result + .getDeclaringType()))) } /** From fab8400ecdc7f26bfcd95f781bb287ba156d5d73 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 6 May 2021 10:45:37 +0200 Subject: [PATCH 244/550] C#: Escape IDs in TRAP label definitions --- .../Context.Factories.cs | 2 +- .../Entities/ArrayType.cs | 2 +- .../Entities/Assembly.cs | 2 +- .../Entities/Base/LabelledEntity.cs | 4 +- .../Entities/ByRefType.cs | 2 +- .../Entities/ConstructedType.cs | 2 +- .../Entities/ErrorType.cs | 2 +- .../Semmle.Extraction.CIL/Entities/Event.cs | 2 +- .../Semmle.Extraction.CIL/Entities/Field.cs | 2 +- .../Semmle.Extraction.CIL/Entities/File.cs | 2 +- .../Semmle.Extraction.CIL/Entities/Folder.cs | 2 +- .../Entities/FunctionPointerType.cs | 2 +- .../Entities/ITypeSignature.cs | 2 +- .../Entities/LocalVariable.cs | 2 +- .../Semmle.Extraction.CIL/Entities/Method.cs | 12 +- .../Entities/MethodSpecificationMethod.cs | 2 +- .../Entities/MethodTypeParameter.cs | 2 +- .../Entities/ModifiedType.cs | 2 +- .../Entities/NamedTypeIdWriter.cs | 2 +- .../Entities/Namespace.cs | 5 +- .../Entities/NoMetadataHandleType.cs | 2 +- .../Entities/Parameter.cs | 2 +- .../Entities/PointerType.cs | 2 +- .../Entities/PrimitiveType.cs | 2 +- .../Entities/Property.cs | 2 +- .../Entities/SignatureDecoder.cs | 24 +- .../Entities/SourceLocation.cs | 2 +- .../Semmle.Extraction.CIL/Entities/Type.cs | 16 +- .../Entities/TypeContainer.cs | 10 - .../Entities/TypeDefinitionType.cs | 2 +- .../Entities/TypeReferenceType.cs | 2 +- .../Entities/TypeTypeParameter.cs | 2 +- .../Entities/Assembly.cs | 2 +- .../Entities/Attribute.cs | 4 +- .../Entities/CommentBlock.cs | 2 +- .../Entities/CommentLine.cs | 2 +- .../Entities/Compilations/Compilation.cs | 2 +- .../Entities/Constructor.cs | 2 +- .../Entities/Event.cs | 2 +- .../Entities/Field.cs | 2 +- .../Entities/Indexer.cs | 4 +- .../Entities/LocalFunction.cs | 4 +- .../Entities/LocalVariable.cs | 4 +- .../Entities/Method.cs | 10 +- .../Entities/Modifier.cs | 2 +- .../Entities/Namespace.cs | 2 +- .../Entities/NamespaceDeclaration.cs | 2 +- .../Entities/NonGeneratedSourceLocation.cs | 2 +- .../Entities/Parameter.cs | 4 +- .../Entities/Property.cs | 2 +- .../Entities/Types/ArrayType.cs | 2 +- .../Entities/Types/DynamicType.cs | 2 +- .../Entities/Types/FunctionPointerType.cs | 2 +- .../Entities/Types/NamedType.cs | 6 +- .../Entities/Types/NullType.cs | 2 +- .../Entities/Types/Nullability.cs | 6 +- .../Entities/Types/PointerType.cs | 2 +- .../Entities/Types/TupleType.cs | 2 +- .../Entities/Types/TypeParameter.cs | 2 +- .../SymbolExtensions.cs | 48 +-- csharp/extractor/Semmle.Extraction/Context.cs | 2 +- .../Semmle.Extraction/Entities/Base/Entity.cs | 14 +- .../Entities/Base/IEntity.cs | 4 +- .../Entities/Base/LabelledEntity.cs | 7 - .../Entities/Base/UnlabelledEntity.cs | 6 +- .../Semmle.Extraction/Entities/File.cs | 2 +- .../Semmle.Extraction/Entities/Folder.cs | 2 +- .../Entities/GeneratedFile.cs | 2 +- .../Entities/GeneratedLocation.cs | 2 +- .../Semmle.Extraction/EscapingTextWriter.cs | 340 ++++++++++++++++++ .../Semmle.Extraction/TrapExtensions.cs | 22 +- .../ql/test/library-tests/regressions/{}.cs | 1 + 72 files changed, 486 insertions(+), 169 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs create mode 100644 csharp/ql/test/library-tests/regressions/{}.cs diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs index 0cdc5bf8dac..5ce1a58491f 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs @@ -36,7 +36,7 @@ namespace Semmle.Extraction.CIL c.Extract(this); }); #if DEBUG_LABELS - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); e.WriteId(writer); var id = writer.ToString(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs index b838a9070ff..0f4138e20c5 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => HashCode.Combine(elementType, rank); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { elementType.WriteId(trapFile, inContext); trapFile.Write('['); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs index d28fa0035c3..c94038902f7 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CIL.Entities file = new File(cx, cx.AssemblyPath); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(FullName); trapFile.Write("#file:///"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs index 150074f22ee..b7e946979fd 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs @@ -25,9 +25,9 @@ namespace Semmle.Extraction.CIL public override string ToString() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); WriteQuotedId(writer); - return writer.ToString(); + return writer.ToString()!; } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs index 2a527419249..5b9ae9fd1aa 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { ElementType.WriteId(trapFile, inContext); trapFile.Write('&'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs index 6e2fb90beae..c97ef697700 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs @@ -100,7 +100,7 @@ namespace Semmle.Extraction.CIL.Entities throw new NotImplementedException(); } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs index e08cea2854c..41b5810ba27 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CIL.Entities { } - public override void WriteId(TextWriter trapFile, bool inContext) => trapFile.Write(""); + public override void WriteId(EscapingTextWriter trapFile, bool inContext) => trapFile.Write(""); public override CilTypeKind Kind => CilTypeKind.ValueOrRefType; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs index a54d0a8065d..0ed8e871d55 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities ed = cx.MdReader.GetEventDefinition(handle); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { parent.WriteId(trapFile); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs index 8decef24128..96ad1715c27 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(DeclaringType); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index a07ffc61de8..c2fd6837e3e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(TransformedPath.DatabaseId); trapFile.Write(";sourcefile"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index b49abce64c9..9c3fbadcf20 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CIL.Entities this.transformedPath = path; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(transformedPath.DatabaseId); trapFile.Write(";folder"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs index 3b6bbba00cc..511826e003c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) { } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { WriteName( trapFile.Write, diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs index beaecdfab6f..004d2707738 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs @@ -4,6 +4,6 @@ namespace Semmle.Extraction.CIL.Entities { internal interface ITypeSignature { - void WriteId(TextWriter trapFile, IGenericContext gc); + void WriteId(EscapingTextWriter trapFile, IGenericContext gc); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs index 5d7a00c64c9..b7690d2498e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CIL.Entities type = t; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(method); trapFile.Write('_'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs index 9df82edaa68..199eb691689 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs @@ -37,17 +37,15 @@ namespace Semmle.Extraction.CIL.Entities public virtual IList? LocalVariables => throw new NotImplementedException(); public IList? Parameters { get; protected set; } - public override void WriteId(TextWriter trapFile) => WriteMethodId(trapFile, DeclaringType, NameLabel); - public abstract string NameLabel { get; } - protected internal void WriteMethodId(TextWriter trapFile, Type parent, string methodName) + public override void WriteId(EscapingTextWriter trapFile) { signature.ReturnType.WriteId(trapFile, this); trapFile.Write(' '); - parent.WriteId(trapFile); + DeclaringType.WriteId(trapFile); trapFile.Write('.'); - trapFile.Write(methodName); + trapFile.Write(NameLabel); if (signature.GenericParameterCount > 0) { @@ -61,11 +59,9 @@ namespace Semmle.Extraction.CIL.Entities trapFile.WriteSeparator(",", ref index); param.WriteId(trapFile, this); } - trapFile.Write(')'); + trapFile.Write(");cil-method"); } - public override string IdSuffix => ";cil-method"; - protected IEnumerable PopulateFlags { get diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs index 840d106b536..2cea67ec16c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs @@ -26,7 +26,7 @@ namespace Semmle.Extraction.CIL.Entities unboundMethod = (Method)Context.CreateGeneric(gc, ms.Method); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { unboundMethod.WriteId(trapFile); trapFile.Write('<'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs index 1795eb29269..a36cf372ea0 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs @@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities private readonly Method method; private readonly int index; - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { if (!(inContext && method == gc)) { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs index f160c6869de..36e08a2e594 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs @@ -37,7 +37,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { Unmodified.WriteId(trapFile, inContext); trapFile.Write(IsRequired ? " modreq" : " modopt"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs index 59c7d172d01..3a71b76b0c3 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CIL.Entities this.type = type; } - public void WriteId(TextWriter trapFile, bool inContext) + public void WriteId(EscapingTextWriter trapFile, bool inContext) { if (type.IsPrimitiveType) { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs index 1d0b373952b..3b7354c791d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities public bool IsGlobalNamespace => ParentNamespace is null; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (ParentNamespace is not null && !ParentNamespace.IsGlobalNamespace) { @@ -22,10 +22,9 @@ namespace Semmle.Extraction.CIL.Entities trapFile.Write('.'); } trapFile.Write(Name); + trapFile.Write(";namespace"); } - public override string IdSuffix => ";namespacee"; - public override bool Equals(object? obj) { if (obj is Namespace ns && Name == ns.Name) diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs index c2a340c4de1..41a28e32bf1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs @@ -137,7 +137,7 @@ namespace Semmle.Extraction.CIL.Entities } } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs index 9cf96412309..b8906f6b845 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs @@ -19,7 +19,7 @@ namespace Semmle.Extraction.CIL.Entities type = t; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(parameterizable); trapFile.Write('_'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs index 8b1209925b6..1a6bd474bd0 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => HashCode.Combine(pointee, nameof(PointerType)); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { pointee.WriteId(trapFile, inContext); trapFile.Write('*'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs index 1e331397237..0e776c1aaad 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => typeCode.GetHashCode(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { Type.WritePrimitiveTypeId(trapFile, Name); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs index 9e56c22a099..99a462069e1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CIL.Entities this.type = type; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(type); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs index ea646449421..0f012c0c18d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CIL.Entities this.shape = shape; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('['); @@ -38,7 +38,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('&'); @@ -54,7 +54,7 @@ namespace Semmle.Extraction.CIL.Entities this.signature = signature; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { FunctionPointerType.WriteName( trapFile.Write, @@ -84,7 +84,7 @@ namespace Semmle.Extraction.CIL.Entities this.typeArguments = typeArguments; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { genericType.WriteId(trapFile, gc); trapFile.Write('<'); @@ -112,7 +112,7 @@ namespace Semmle.Extraction.CIL.Entities this.index = index; } - public void WriteId(TextWriter trapFile, IGenericContext outerGc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext outerGc) { if (!ReferenceEquals(innerGc, outerGc) && innerGc is Method method) { @@ -132,7 +132,7 @@ namespace Semmle.Extraction.CIL.Entities this.index = index; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { trapFile.Write("T!"); trapFile.Write(index); @@ -158,7 +158,7 @@ namespace Semmle.Extraction.CIL.Entities this.isRequired = isRequired; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { unmodifiedType.WriteId(trapFile, gc); trapFile.Write(isRequired ? " modreq(" : " modopt("); @@ -186,7 +186,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('*'); @@ -207,7 +207,7 @@ namespace Semmle.Extraction.CIL.Entities this.typeCode = typeCode; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { trapFile.Write(typeCode.Id()); } @@ -227,7 +227,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write("[]"); @@ -248,7 +248,7 @@ namespace Semmle.Extraction.CIL.Entities this.handle = handle; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { var type = (Type)gc.Context.Create(handle); type.WriteId(trapFile); @@ -269,7 +269,7 @@ namespace Semmle.Extraction.CIL.Entities this.handle = handle; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { var type = (Type)gc.Context.Create(handle); type.WriteId(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs index ff67844121a..5420ac53645 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CIL.Entities file = cx.CreateSourceFile(location.File); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { file.WriteId(trapFile); trapFile.Write(','); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index db4876f8a9a..5d27c5400d5 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -43,11 +43,13 @@ namespace Semmle.Extraction.CIL.Entities /// (This is to avoid infinite recursion generating a method ID that returns a /// type parameter.) /// - public abstract void WriteId(TextWriter trapFile, bool inContext); + public abstract void WriteId(EscapingTextWriter trapFile, bool inContext); - public sealed override void WriteId(TextWriter trapFile) => WriteId(trapFile, false); - - public override string IdSuffix => ";cil-type"; + public sealed override void WriteId(EscapingTextWriter trapFile) + { + WriteId(trapFile, false); + trapFile.Write(";cil-type"); + } /// /// Returns the friendly qualified name of types, such as @@ -58,10 +60,12 @@ namespace Semmle.Extraction.CIL.Entities /// public string GetQualifiedName() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); WriteId(writer, false); var name = writer.ToString(); - return name.Substring(name.IndexOf(AssemblyTypeNameSeparator) + 2); + return name.Substring(name.IndexOf(AssemblyTypeNameSeparator) + 2). + Replace(";namespace", ""). + Replace(";cil-type", ""); } public abstract CilTypeKind Kind { get; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs index e201e065255..aae0b4b0b48 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs @@ -12,16 +12,6 @@ namespace Semmle.Extraction.CIL.Entities { } - public abstract string IdSuffix { get; } - - public override void WriteQuotedId(TextWriter trapFile) - { - trapFile.Write("@\""); - WriteId(trapFile); - trapFile.Write(IdSuffix); - trapFile.Write('\"'); - } - public abstract IEnumerable MethodParameters { get; } public abstract IEnumerable TypeParameters { get; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs index 51b02f1482e..a5d377846d7 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs @@ -40,7 +40,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => handle.GetHashCode(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs index c94a6978f5e..1af91d32586 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs @@ -88,7 +88,7 @@ namespace Semmle.Extraction.CIL.Entities public override IEnumerable ThisGenericArguments => typeParams.Value; - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs index 15db1536a3f..580c8573acf 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs @@ -25,7 +25,7 @@ namespace Semmle.Extraction.CIL.Entities return type.GetHashCode() * 13 + index; } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { type.WriteId(trapFile, inContext); trapFile.Write('!'); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs index c0ffce70dbe..9555fabec4f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs @@ -69,7 +69,7 @@ namespace Semmle.Extraction.CSharp.Entities return AssemblyConstructorFactory.Instance.CreateEntity(cx, outputAssemblyCacheKey, null); } - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(assembly.ToString()); if (!(assemblyPath is null)) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs index b1bfb508d75..7e7fc39aab8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CSharp.Entities this.entity = entity; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (ReportingLocation?.IsInSource == true) { @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { if (ReportingLocation?.IsInSource == true) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index a3394885d1c..6d4a2cf07f9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(Symbol.Location)); trapFile.Write(";commentblock"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index a1c9fb5c643..b9fb6cdfee2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(Location)); trapFile.Write(";commentline"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs index 1c929c1ad5a..f3672e4c6e4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs @@ -81,7 +81,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.compilation_finished(this, (float)p.Total.Cpu.TotalSeconds, (float)p.Total.Elapsed.TotalSeconds); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(hashCode); trapFile.Write(";compilation"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 39c202929d2..3f31108b57e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -144,7 +144,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (Symbol.IsStatic) trapFile.Write("static"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index e92a42d8ab1..ca9753d229b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities private Event(Context cx, IEventSymbol init) : base(cx, init) { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 2fe1cf224ab..851d1103e1d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -118,7 +118,7 @@ namespace Semmle.Extraction.CSharp.Entities private readonly Lazy type; public Type Type => type.Value; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Type); trapFile.Write(" "); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index 3da002f8b2c..8c00bb94335 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -73,13 +73,13 @@ namespace Semmle.Extraction.CSharp.Entities public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); trapFile.Write(Symbol.MetadataName); trapFile.Write('('); - trapFile.BuildList(",", Symbol.Parameters, (p, tb0) => tb0.WriteSubId(Type.Create(Context, p.Type))); + trapFile.BuildList(",", Symbol.Parameters, p => trapFile.WriteSubId(Type.Create(Context, p.Type))); trapFile.Write(");indexer"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index a94401f2a93..a94c170f408 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -10,12 +10,12 @@ namespace Semmle.Extraction.CSharp.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { throw new InvalidOperationException(); } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { trapFile.Write('*'); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index ee685731c60..81a47710ba1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -8,12 +8,12 @@ namespace Semmle.Extraction.CSharp.Entities { private LocalVariable(Context cx, ISymbol init) : base(cx, init) { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { throw new InvalidOperationException(); } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { trapFile.Write('*'); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 61c8d768521..12d0ce4cc10 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -127,7 +127,7 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Factored out to share logic between `Method` and `UserOperator`. /// - private static void BuildMethodId(Method m, TextWriter trapFile) + private static void BuildMethodId(Method m, EscapingTextWriter trapFile) { m.Symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write(" "); @@ -153,7 +153,7 @@ namespace Semmle.Extraction.CSharp.Entities // Type arguments with different nullability can result in // a constructed method with different nullability of its parameters and return type, // so we need to create a distinct database entity for it. - trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { ta.Symbol.BuildOrWriteId(m.Context, tb0, m.Symbol); trapFile.Write((int)ta.Nullability); }); + trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), ta => { ta.Symbol.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write((int)ta.Nullability); }); trapFile.Write('>'); } } @@ -182,12 +182,12 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { BuildMethodId(this, trapFile); } - protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethodSymbol method) + protected static void AddParametersToId(Context cx, EscapingTextWriter trapFile, IMethodSymbol method) { trapFile.Write('('); var index = 0; @@ -222,7 +222,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.Write(')'); } - public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter trapFile, IEnumerable explicitInterfaceImplementations) + public static void AddExplicitInterfaceQualifierToId(Context cx, EscapingTextWriter trapFile, IEnumerable explicitInterfaceImplementations) { if (explicitInterfaceImplementations.Any()) trapFile.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType))); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index 1d56da2623f..806e73e3590 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities public override Location? ReportingLocation => null; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Symbol); trapFile.Write(";modifier"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 7d77ef276cb..68b108743f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (!Symbol.IsGlobalNamespace) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs index 749c7ae22c8..b9fd57b2cea 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CSharp.Entities this.parent = parent; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(ReportingLocation)); trapFile.Write(";namespacedeclaration"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs index 141c428dd55..7642c30d1a7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CSharp.Entities get; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("loc,"); trapFile.WriteSubId(FileEntity); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 462c8bbb297..9d69372174e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -74,7 +74,7 @@ namespace Semmle.Extraction.CSharp.Entities public static Parameter Create(Context cx, IParameterSymbol param) => ParameterFactory.Instance.CreateEntity(cx, param, (param, null, null)); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (Parent is null) Parent = Method.Create(Context, Symbol.ContainingSymbol as IMethodSymbol); @@ -209,7 +209,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("__arglist;type"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 5fb77cf4dc2..9e4a1c79ad2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities private Type Type => type.Value; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Type); trapFile.Write(" "); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index 48381430428..18a98883c8b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateType(trapFile); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ElementType); Symbol.BuildArraySuffix(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index b43c4c741f4..8a28beaff7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.parent_namespace(this, Namespace.Create(Context, Context.Compilation.GlobalNamespace)); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("dynamic;type"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs index 2bb9cd3a1b6..37ba909ec46 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { Symbol.BuildTypeId(Context, trapFile, Symbol); trapFile.Write(";functionpointertype"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index b607da5d998..01ca82b71ba 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -128,7 +128,7 @@ namespace Semmle.Extraction.CSharp.Entities private bool IsAnonymousType() => Symbol.IsAnonymousType || Symbol.Name.Contains("__AnonymousType"); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (IsAnonymousType()) { @@ -141,7 +141,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { if (IsAnonymousType()) trapFile.Write('*'); @@ -195,7 +195,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(referencedType); trapFile.Write(";typeRef"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index f65d7b17db6..6a049bc3939 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.types(this, Kinds.TypeKind.NULL, "null"); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(";type"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs index 3cd85bdbdde..c0ef8299eed 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs @@ -79,7 +79,7 @@ namespace Semmle.Extraction.CSharp.Entities return h; } - public void WriteId(TextWriter trapFile) + public void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Annotation); trapFile.Write('('); @@ -90,7 +90,7 @@ namespace Semmle.Extraction.CSharp.Entities public override string ToString() { - using var w = new StringWriter(); + using var w = new EscapingTextWriter(); WriteId(w); return w.ToString(); } @@ -120,7 +120,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { Symbol.WriteId(trapFile); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index 635172a0e62..c4f8aa74245 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities PointedAtType = Create(cx, Symbol.PointedAtType); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(PointedAtType); trapFile.Write("*;type"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index ca3e182a99d..ddbf1ac52a6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities // All tuple types are "local types" public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { Symbol.BuildTypeId(Context, trapFile, Symbol); trapFile.Write(";tuple"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index dcddf5d5cf2..146b1905018 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -103,7 +103,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { string kind; IEntity containingEntity; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index f03f1674969..32b90e37068 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -160,10 +160,10 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType); - private static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) { using (cx.StackGuard) { @@ -207,7 +207,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol? symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) { if (symbol is null) { @@ -249,7 +249,7 @@ namespace Semmle.Extraction.CSharp /// it will generate an appropriate ID that encodes the signature of /// . /// - public static void BuildOrWriteId(this ISymbol? symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) => + public static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) => symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); /// @@ -264,7 +264,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write(']'); } - private static void BuildAssembly(IAssemblySymbol asm, TextWriter trapFile, bool extraPrecise = false) + private static void BuildAssembly(IAssemblySymbol asm, EscapingTextWriter trapFile, bool extraPrecise = false) { var assembly = asm.Identity; trapFile.Write(assembly.Name); @@ -282,22 +282,22 @@ namespace Semmle.Extraction.CSharp trapFile.Write("::"); } - private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) + private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) { - BuildFunctionPointerSignature(funptr, trapFile, (s, tw) => s.BuildOrWriteId(cx, tw, symbolBeingDefined)); + BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined)); } - private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) { if (!constructUnderlyingTupleType && named.IsTupleType) { trapFile.Write('('); trapFile.BuildList(",", named.TupleElements, - (f, tb0) => + f => { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); } ); trapFile.Write(")"); @@ -340,7 +340,7 @@ namespace Semmle.Extraction.CSharp // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - (ta, tb0) => ta.Symbol.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass) ); trapFile.Write('>'); } @@ -364,7 +364,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildNamespace(this INamespaceSymbol ns, Context cx, TextWriter trapFile) + private static void BuildNamespace(this INamespaceSymbol ns, Context cx, EscapingTextWriter trapFile) { trapFile.WriteSubId(Namespace.Create(cx, ns)); trapFile.Write('.'); @@ -377,7 +377,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write("<>__AnonType"); trapFile.Write(hackTypeNumber); trapFile.Write('<'); - trapFile.BuildList(",", type.GetMembers().OfType(), (prop, tb0) => BuildDisplayName(prop.Type, cx, tb0)); + trapFile.BuildList(",", type.GetMembers().OfType(), prop => BuildDisplayName(prop.Type, cx, trapFile)); trapFile.Write('>'); } @@ -433,7 +433,7 @@ namespace Semmle.Extraction.CSharp } public static void BuildFunctionPointerSignature(IFunctionPointerTypeSymbol funptr, TextWriter trapFile, - Action buildNested) + Action buildNested) { trapFile.Write("delegate* "); trapFile.Write(funptr.Signature.CallingConvention.ToString().ToLowerInvariant()); @@ -447,19 +447,19 @@ namespace Semmle.Extraction.CSharp trapFile.Write('<'); trapFile.BuildList(",", funptr.Signature.Parameters, - (p, trap) => + p => { - buildNested(p.Type, trap); + buildNested(p.Type); switch (p.RefKind) { case RefKind.Out: - trap.Write(" out"); + trapFile.Write(" out"); break; case RefKind.In: - trap.Write(" in"); + trapFile.Write(" in"); break; case RefKind.Ref: - trap.Write(" ref"); + trapFile.Write(" ref"); break; } }); @@ -469,7 +469,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write(","); } - buildNested(funptr.Signature.ReturnType, trapFile); + buildNested(funptr.Signature.ReturnType); if (funptr.Signature.ReturnsByRef) trapFile.Write(" ref"); @@ -481,7 +481,7 @@ namespace Semmle.Extraction.CSharp private static void BuildFunctionPointerTypeDisplayName(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile) { - BuildFunctionPointerSignature(funptr, trapFile, (s, tw) => s.BuildDisplayName(cx, tw)); + BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildDisplayName(cx, trapFile)); } private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType) @@ -492,7 +492,7 @@ namespace Semmle.Extraction.CSharp trapFile.BuildList( ",", namedType.TupleElements.Select(f => f.Type), - (t, tb0) => t.BuildDisplayName(cx, tb0)); + t => t.BuildDisplayName(cx, trapFile)); trapFile.Write(")"); return; } @@ -512,11 +512,11 @@ namespace Semmle.Extraction.CSharp trapFile.BuildList( ",", namedType.TypeArguments, - (p, tb0) => + p => { if (IsReallyBound(namedType)) { - p.BuildDisplayName(cx, tb0); + p.BuildDisplayName(cx, trapFile); } }); trapFile.Write('>'); diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index a60d82c6952..b08fb98fc55 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -108,7 +108,7 @@ namespace Semmle.Extraction Populate(init as ISymbol, entity); #if DEBUG_LABELS - using var id = new StringWriter(); + using var id = new EscapingTextWriter(); entity.WriteQuotedId(id); CheckEntityHasUniqueLabel(id.ToString(), entity); #endif diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs index 1d5081df2e5..c5d630bc7b1 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs @@ -15,9 +15,14 @@ namespace Semmle.Extraction public Label Label { get; set; } - public abstract void WriteId(TextWriter trapFile); + public abstract void WriteId(EscapingTextWriter trapFile); - public abstract void WriteQuotedId(TextWriter trapFile); + public virtual void WriteQuotedId(EscapingTextWriter trapFile) + { + trapFile.WriteUnescaped("@\""); + WriteId(trapFile); + trapFile.WriteUnescaped('\"'); + } public abstract Location? ReportingLocation { get; } @@ -27,9 +32,10 @@ namespace Semmle.Extraction { trapFile.WriteLabel(this); trapFile.Write("="); + using var escaping = new EscapingTextWriter(trapFile); try { - WriteQuotedId(trapFile); + WriteQuotedId(escaping); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -51,7 +57,7 @@ namespace Semmle.Extraction /// public string GetDebugLabel() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); writer.WriteLabel(Label.Value); writer.Write('='); WriteQuotedId(writer); diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs index 5aadbeb9e4d..dcf8dcbc373 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs @@ -29,14 +29,14 @@ namespace Semmle.Extraction /// Writes the unique identifier of this entitiy to a trap file. /// /// The trapfile to write to. - void WriteId(TextWriter trapFile); + void WriteId(EscapingTextWriter trapFile); /// /// Writes the quoted identifier of this entity, /// which could be @"..." or * /// /// The trapfile to write to. - void WriteQuotedId(TextWriter trapFile); + void WriteQuotedId(EscapingTextWriter trapFile); /// /// The location for reporting purposes. diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs index 12a98ce2303..62d9cbd64be 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs @@ -7,12 +7,5 @@ namespace Semmle.Extraction protected LabelledEntity(Context cx) : base(cx) { } - - public override void WriteQuotedId(TextWriter trapFile) - { - trapFile.Write("@\""); - WriteId(trapFile); - trapFile.Write('\"'); - } } } diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs index 6b6a22eb4b5..506a84bf7ad 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs @@ -9,14 +9,14 @@ namespace Semmle.Extraction cx.AddFreshLabel(this); } - public sealed override void WriteId(TextWriter writer) + public sealed override void WriteId(EscapingTextWriter writer) { writer.Write('*'); } - public sealed override void WriteQuotedId(TextWriter writer) + public sealed override void WriteQuotedId(EscapingTextWriter writer) { - WriteId(writer); + writer.Write('*'); } } } diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index ed8881f0d2f..11d37c99636 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.Entities public override bool NeedsPopulation => true; - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(TransformedPath.DatabaseId); trapFile.Write(";sourcefile"); diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs index 82364c987e7..07e8c805e7f 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.Entities public override bool NeedsPopulation => true; - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Symbol.DatabaseId); trapFile.Write(";folder"); diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs index db458e574a5..7c0e5df7be9 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.Entities trapFile.files(this, "", "", ""); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("GENERATED;sourcefile"); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs index 331e70262dc..db552f7e452 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.Entities trapFile.locations_default(this, generatedFile, 0, 0, 0, 0); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("loc,"); trapFile.WriteSubId(generatedFile); diff --git a/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs new file mode 100644 index 00000000000..5f6805de658 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs @@ -0,0 +1,340 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Semmle.Extraction +{ + /// + /// A `TextWriter` object that wraps another `TextWriter` object, and which + /// HTML escapes the characters `&`, `{`, `}`, `"`, `@`, and `#`, before + /// writing to the underlying object. + /// + public class EscapingTextWriter : TextWriter + { + private readonly TextWriter wrapped; + private readonly bool disposeUnderlying; + + public EscapingTextWriter(TextWriter wrapped, bool disposeUnderlying = false) + { + this.wrapped = wrapped; + this.disposeUnderlying = disposeUnderlying; + } + + /// + /// Creates a new instance with a new underlying `StringWriter` object. The + /// underlying object is disposed of when this object is. + /// + public EscapingTextWriter() : this(new StringWriter(), true) { } + + public EscapingTextWriter(IFormatProvider? formatProvider) : base(formatProvider) + => throw new NotImplementedException(); + + private void WriteEscaped(char c) + { + switch (c) + { + case '&': + wrapped.Write("&"); + break; + case '{': + wrapped.Write("{"); + break; + case '}': + wrapped.Write("}"); + break; + case '"': + wrapped.Write("""); + break; + case '@': + wrapped.Write("@"); + break; + case '#': + wrapped.Write("#"); + break; + default: + wrapped.Write(c); + break; + }; + } + + public void WriteSubId(IEntity entity) + { + if (entity is null) + { + wrapped.Write(""); + return; + } + + WriteUnescaped('{'); + wrapped.WriteLabel(entity); + WriteUnescaped('}'); + } + + public void WriteUnescaped(char c) + => wrapped.Write(c); + + public void WriteUnescaped(string s) + => wrapped.Write(s); + + #region overrides + + public override Encoding Encoding => wrapped.Encoding; + + public override IFormatProvider FormatProvider => wrapped.FormatProvider; + + public override string NewLine { get => wrapped.NewLine; } + + public override void Close() + => throw new NotImplementedException(); + + public override ValueTask DisposeAsync() + => throw new NotImplementedException(); + + public override bool Equals(object? obj) + => wrapped.Equals(obj); + + public override void Flush() + => wrapped.Flush(); + + public override Task FlushAsync() + => wrapped.FlushAsync(); + + public override int GetHashCode() + => wrapped.GetHashCode(); + + public override string ToString() + => wrapped.ToString() ?? ""; + + public override void Write(bool value) + => wrapped.Write(value); + + public override void Write(char value) + => WriteEscaped(value); + + public override void Write(char[]? buffer) + { + if (buffer is null) + return; + Write(buffer, 0, buffer.Length); + } + + public override void Write(char[] buffer, int index, int count) + { + for (var i = index; i < buffer.Length && i < index + count; i++) + { + WriteEscaped(buffer[i]); + } + } + + + public override void Write(decimal value) + => wrapped.Write(value); + + public override void Write(double value) + => wrapped.Write(value); + + public override void Write(int value) + => wrapped.Write(value); + + public override void Write(long value) + => wrapped.Write(value); + + public override void Write(object? value) + => Write(value?.ToString()); + + public override void Write(ReadOnlySpan buffer) + { + foreach (var c in buffer) + { + WriteEscaped(c); + } + } + + public override void Write(float value) + => wrapped.Write(value); + + public override void Write(string? value) + { + if (value is null) + { + wrapped.Write(value); + } + else + { + foreach (var c in value) + { + WriteEscaped(c); + } + } + } + + public override void Write(string format, object? arg0) + => Write(string.Format(format, arg0)); + + public override void Write(string format, object? arg0, object? arg1) + => Write(string.Format(format, arg0, arg1)); + + public override void Write(string format, object? arg0, object? arg1, object? arg2) + => Write(string.Format(format, arg0, arg1, arg2)); + + public override void Write(string format, params object?[] arg) + => Write(string.Format(format, arg)); + + public override void Write(StringBuilder? value) + { + if (value is null) + { + wrapped.Write(value); + } + else + { + for (var i = 0; i < value.Length; i++) + { + WriteEscaped(value[i]); + } + } + } + + public override void Write(uint value) + => wrapped.Write(value); + + public override void Write(ulong value) + => wrapped.Write(value); + + public override Task WriteAsync(char value) + => throw new NotImplementedException(); + + public override Task WriteAsync(char[] buffer, int index, int count) + => throw new NotImplementedException(); + + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override Task WriteAsync(string? value) + => throw new NotImplementedException(); + + public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override void WriteLine() + => wrapped.WriteLine(); + + public override void WriteLine(bool value) + => wrapped.WriteLine(value); + + public override void WriteLine(char value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(char[]? buffer) + { + Write(buffer); + WriteLine(); + } + + public override void WriteLine(char[] buffer, int index, int count) + { + Write(buffer, index, count); + WriteLine(); + } + + public override void WriteLine(decimal value) + => wrapped.WriteLine(value); + + public override void WriteLine(double value) + => wrapped.WriteLine(value); + + public override void WriteLine(int value) + => wrapped.WriteLine(value); + + public override void WriteLine(long value) + => wrapped.WriteLine(value); + + public override void WriteLine(object? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(ReadOnlySpan buffer) + { + Write(buffer); + WriteLine(); + } + + public override void WriteLine(float value) + => wrapped.WriteLine(value); + + public override void WriteLine(string? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0) + { + Write(format, arg0); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0, object? arg1) + { + Write(format, arg0, arg1); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0, object? arg1, object? arg2) + { + Write(format, arg0, arg1, arg2); + WriteLine(); + } + + public override void WriteLine(string format, params object?[] arg) + { + Write(format, arg); + WriteLine(); + } + + public override void WriteLine(StringBuilder? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(uint value) + => wrapped.WriteLine(value); + + public override void WriteLine(ulong value) + => wrapped.WriteLine(value); + + public override Task WriteLineAsync() + => throw new NotImplementedException(); + + public override Task WriteLineAsync(char value) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(char[] buffer, int index, int count) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(string? value) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + protected override void Dispose(bool disposing) + { + if (disposing && disposeUnderlying) + wrapped.Dispose(); + } + + #endregion overrides + } +} diff --git a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs index 11111467147..e7d3fcec6b5 100644 --- a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs +++ b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs @@ -17,19 +17,6 @@ namespace Semmle.Extraction trapFile.WriteLabel(entity.Label.Value); } - public static void WriteSubId(this TextWriter trapFile, IEntity entity) - { - if (entity is null) - { - trapFile.Write(""); - return; - } - - trapFile.Write('{'); - trapFile.WriteLabel(entity); - trapFile.Write('}'); - } - public static void WriteSeparator(this TextWriter trapFile, string separator, ref int index) { if (index++ > 0) @@ -231,9 +218,9 @@ namespace Semmle.Extraction /// The separator string (e.g. ",") /// The list of items. /// The original trap builder (fluent interface). - public static TextWriter AppendList(this TextWriter trapFile, string separator, IEnumerable items) where T : IEntity + public static TextWriter AppendList(this EscapingTextWriter trapFile, string separator, IEnumerable items) where T : IEntity { - return trapFile.BuildList(separator, items, (x, tb0) => { tb0.WriteSubId(x); }); + return trapFile.BuildList(separator, items, x => trapFile.WriteSubId(x)); } /// @@ -245,7 +232,8 @@ namespace Semmle.Extraction /// The list of items. /// The action on each item. /// The original trap builder (fluent interface). - public static TextWriter BuildList(this TextWriter trapFile, string separator, IEnumerable items, Action action) + public static T1 BuildList(this T1 trapFile, string separator, IEnumerable items, Action action) + where T1 : TextWriter { var first = true; foreach (var item in items) @@ -254,7 +242,7 @@ namespace Semmle.Extraction first = false; else trapFile.Write(separator); - action(item, trapFile); + action(item); } return trapFile; } diff --git a/csharp/ql/test/library-tests/regressions/{}.cs b/csharp/ql/test/library-tests/regressions/{}.cs new file mode 100644 index 00000000000..266f8ccf4b1 --- /dev/null +++ b/csharp/ql/test/library-tests/regressions/{}.cs @@ -0,0 +1 @@ +class CheckFileNameEscaping { } \ No newline at end of file From 2d1ba59e6d8ccc2bf77e84db9220a7bbb1634dd1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 6 May 2021 21:55:30 +0200 Subject: [PATCH 245/550] Apply suggestions from code review Co-authored-by: Esben Sparre Andreasen --- .../ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp | 4 ++-- javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp index 12e030150b6..76a7e65fd3f 100644 --- a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp @@ -4,7 +4,7 @@

    - Dynamically constructing HTML with inputs from exported functions may + Dynamically constructing HTML with inputs from library functions may inadvertently leave a client open to XSS attacks. Clients using the exported function may use inputs containing unsafe HTML, @@ -28,7 +28,7 @@

    - The following example shows a library function that shows a boldface name + The following example has a library function that renders a boldface name by writing to the innerHTML property of an element.

    diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql index 464f4951307..d6199af4598 100644 --- a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.ql @@ -1,7 +1,7 @@ /** * @name Unsafe HTML constructed from library input * @description Using externally controlled strings to construct HTML might allow a malicious - * user to perform an cross-site scripting attack. + * user to perform a cross-site scripting attack. * @kind path-problem * @problem.severity error * @precision high From be69c3a458385525bc893d0dfb8fbf82e9dd05d1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 6 May 2021 21:59:35 +0200 Subject: [PATCH 246/550] Apply suggestions from code review Co-authored-by: Esben Sparre Andreasen --- .../dataflow/UnsafeHtmlConstructionCustomizations.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index a99b5e7986d..99e72970642 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -38,7 +38,7 @@ module UnsafeHtmlConstruction { /** * A sink for unsafe HTML constructed from library input. - * This sink somehow transforms its input into a value that can cause XSS if it ends up in a XSS sink. + * This sink transforms its input into a value that can cause XSS if it ends up in a XSS sink. */ abstract class Sink extends DataFlow::Node { /** @@ -165,6 +165,7 @@ module UnsafeHtmlConstruction { MarkdownSink() { exists(DataFlow::Node pred, DataFlow::Node succ, Markdown::MarkdownStep step | step.step(pred, succ) and + step.preservesHtml() and this = pred and succ = isUsedInXssSink(xssSink) ) @@ -176,7 +177,7 @@ module UnsafeHtmlConstruction { /** * Holds if there is a path without unmatched return steps from `source` to `sink`. */ - predicate requireMatchedReturn(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { + predicate hasPathWithoutUnmatchedReturn(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { exists(DataFlow::MidPathNode mid | source.getASuccessor*() = mid and sink = mid.getASuccessor() and From b53759c5a059b40cc6865371fd46f0d848e6a737 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 6 May 2021 22:49:25 +0200 Subject: [PATCH 247/550] corrections after code review --- .../ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp | 4 ++-- javascript/ql/src/semmle/javascript/frameworks/Markdown.qll | 5 +++++ .../javascript/security/dataflow/UnsafeHtmlConstruction.qll | 2 +- .../dataflow/UnsafeHtmlConstructionCustomizations.qll | 6 ++++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp index 76a7e65fd3f..619b55e4920 100644 --- a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp @@ -4,8 +4,8 @@

    - Dynamically constructing HTML with inputs from library functions may - inadvertently leave a client open to XSS attacks. + Dynamically constructing HTML with inputs from library functions that are + available to external clients may inadvertently leave a client open to XSS attacks. Clients using the exported function may use inputs containing unsafe HTML, and if these inputs end up in the DOM, the client may be vulnerable to diff --git a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll index c98243e4fbb..1a6c9d7a8c2 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll @@ -17,6 +17,11 @@ module Markdown { * Holds if there is a taint-step from `pred` to `succ` for a taint-preserving markdown parser. */ abstract predicate step(DataFlow::Node pred, DataFlow::Node succ); + + /** + * Holds if the taint-step preserves HTML. + */ + predicate preservesHTML() { any() } } private class MarkdownStepAsTaintStep extends TaintTracking::SharedTaintStep { diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll index 699d9d3e510..53debb48ff6 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -38,7 +38,7 @@ module UnsafeHtmlConstruction { // override to require that there is a path without unmatched return steps override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { super.hasFlowPath(source, sink) and - requireMatchedReturn(source, sink) + hasPathWithoutUnmatchedReturn(source, sink) } override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 99e72970642..8546041ef06 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -165,7 +165,7 @@ module UnsafeHtmlConstruction { MarkdownSink() { exists(DataFlow::Node pred, DataFlow::Node succ, Markdown::MarkdownStep step | step.step(pred, succ) and - step.preservesHtml() and + step.preservesHTML() and this = pred and succ = isUsedInXssSink(xssSink) ) @@ -177,7 +177,9 @@ module UnsafeHtmlConstruction { /** * Holds if there is a path without unmatched return steps from `source` to `sink`. */ - predicate hasPathWithoutUnmatchedReturn(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { + predicate hasPathWithoutUnmatchedReturn( + DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink + ) { exists(DataFlow::MidPathNode mid | source.getASuccessor*() = mid and sink = mid.getASuccessor() and From 9ac55aff0e17be8796a89a471e58103e9a00f7cb Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 6 May 2021 17:43:28 -0700 Subject: [PATCH 248/550] C++: One more join order fix --- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index f5fb7309cff..a7da1ad16d8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -656,7 +656,7 @@ private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) { exists(LoadInstruction load | load.getSourceValueOperand() = opTo and opTo.getAnyDef() = iFrom and - isSingleFieldClass(iFrom.getResultType(), opTo) + isSingleFieldClass(pragma[only_bind_out](pragma[only_bind_out](iFrom).getResultType()), opTo) ) } From 08fa611700b36174985cdcf4e5b2f3326e5ff472 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 11:18:50 +0200 Subject: [PATCH 249/550] C++: Avoid calling SwitchCase.getAStmt for performance reasons. This turns out to not be needed as the statements inside the switch case will get picked up by the BlockStmt.getAStmt case already. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 97ff9e06d64..716e8927480 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -98,8 +98,6 @@ predicate stmtMayThrow(Stmt stmt) { stmtMayThrow(switchStmt.getStmt()) ) or - stmtMayThrow(stmt.(SwitchCase).getAStmt()) - or // NOTE: We don't include `TryStmt` as those exceptions are not "observable" outside the function. stmtMayThrow(stmt.(Handler).getBlock()) or From ca895608494b07ab142034c75d9a6be305a3a4fd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 7 May 2021 11:42:53 +0200 Subject: [PATCH 250/550] C#: Remove unnecessary `!` --- .../Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs index b7e946979fd..780ced94916 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs @@ -27,7 +27,7 @@ namespace Semmle.Extraction.CIL { using var writer = new EscapingTextWriter(); WriteQuotedId(writer); - return writer.ToString()!; + return writer.ToString(); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; From 80d41d9fe5bf7c648e58eecacf25ba55f97085c7 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 11:48:09 +0200 Subject: [PATCH 251/550] C++: Add false positive testcase involving assignments. --- .../semmle/tests/IncorrectAllocationErrorHandling.expected | 1 + .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index 4720e02b381..51d9a10bc27 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -16,3 +16,4 @@ | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | | test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | | test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | +| test.cpp:219:9:219:15 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:220:34:220:36 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index da990934515..90b8e87a13c 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -212,3 +212,10 @@ void bad_new_catch_baseclass_of_bad_alloc() { int* p = new(std::nothrow) int; // BAD } catch(const std::exception&) { } } + +void good_new_catch_exception_in_assignment() { + int* p; + try { + p = new int; // GOOD [FALSE POSITIVE] + } catch(const std::bad_alloc&) { } +} \ No newline at end of file From 88e6cbaacda1bb8be882558206eb36ab63750664 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 11:49:25 +0200 Subject: [PATCH 252/550] C++: Include Assignments in exprMayThrow and accept test changes. --- .../Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 4 ++++ .../semmle/tests/IncorrectAllocationErrorHandling.expected | 1 - .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 716e8927480..a7bd82987be 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -127,6 +127,10 @@ predicate exprMayThrow(Expr e) { convertedExprMayThrow([binOp.getLeftOperand(), binOp.getRightOperand()]) ) or + exists(Assignment assign | assign = e | + convertedExprMayThrow([assign.getLValue(), assign.getRValue()]) + ) + or exists(CommaExpr comma | comma = e | convertedExprMayThrow([comma.getLeftOperand(), comma.getRightOperand()]) ) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index 51d9a10bc27..4720e02b381 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -16,4 +16,3 @@ | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | | test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | | test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | -| test.cpp:219:9:219:15 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:220:34:220:36 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 90b8e87a13c..285069f4cb3 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -216,6 +216,6 @@ void bad_new_catch_baseclass_of_bad_alloc() { void good_new_catch_exception_in_assignment() { int* p; try { - p = new int; // GOOD [FALSE POSITIVE] + p = new int; // GOOD } catch(const std::bad_alloc&) { } } \ No newline at end of file From 7adb7b67f21d1e90cf343ea6dbf9c3775b66802f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 12:19:19 +0200 Subject: [PATCH 253/550] C++: Add false positive testcase involving conversions. --- .../semmle/tests/IncorrectAllocationErrorHandling.expected | 1 + .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index 4720e02b381..895eeaf1665 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -16,3 +16,4 @@ | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | | test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | | test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | +| test.cpp:225:23:225:29 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:226:34:226:36 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 285069f4cb3..db73bbb8b55 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -218,4 +218,10 @@ void good_new_catch_exception_in_assignment() { try { p = new int; // GOOD } catch(const std::bad_alloc&) { } +} + +void good_new_catch_exception_in_conversion() { + try { + long* p = (long*) new int; // GOOD [FALSE POSITIVE] + } catch(const std::bad_alloc&) { } } \ No newline at end of file From 90e8368258ea513208802fd5cf19a8a2ee496976 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 12:31:43 +0200 Subject: [PATCH 254/550] C++: Properly handle conversions in convertedExprMayThrow. This recursive implementation idea is stolen from convertedExprMightOverflow in SimpleRangeAnalysis. --- .../CWE/CWE-570/IncorrectAllocationErrorHandling.ql | 6 +++++- .../semmle/tests/IncorrectAllocationErrorHandling.expected | 2 -- .../query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index a7bd82987be..2f67b02e856 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -107,7 +107,11 @@ predicate stmtMayThrow(Stmt stmt) { } /** Holds if the evaluation of `e` (including conversions) may throw an exception. */ -predicate convertedExprMayThrow(Expr e) { exprMayThrow(e.getFullyConverted()) } +predicate convertedExprMayThrow(Expr e) { + exprMayThrow(e) + or + convertedExprMayThrow(e.getConversion()) +} /** Holds if the evaluation of `e` may throw an exception. */ predicate exprMayThrow(Expr e) { diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index 895eeaf1665..d597dee46f7 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -14,6 +14,4 @@ | test.cpp:93:15:93:41 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | -| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | | test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | -| test.cpp:225:23:225:29 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:226:34:226:36 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index db73bbb8b55..98755a5692e 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -196,7 +196,7 @@ void good_new_with_throwing_call() { void bad_new_with_nonthrowing_call() { try { - int* p1 = new(std::nothrow) int; // BAD + int* p1 = new(std::nothrow) int; // BAD [NOT DETECTED] calls_non_throwing(p1); } catch(...) { } @@ -222,6 +222,6 @@ void good_new_catch_exception_in_assignment() { void good_new_catch_exception_in_conversion() { try { - long* p = (long*) new int; // GOOD [FALSE POSITIVE] + long* p = (long*) new int; // GOOD } catch(const std::bad_alloc&) { } } \ No newline at end of file From fc7d9c2c09f9163247b7c5e69f463643d3a2d25b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 7 May 2021 12:34:38 +0200 Subject: [PATCH 255/550] C++: Fix missing result by properly specifying that the function with unknown code actually didn't throw an exception. --- .../tests/IncorrectAllocationErrorHandling.expected | 1 + .../Security/CWE/CWE-570/semmle/tests/test.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected index d597dee46f7..4720e02b381 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected @@ -14,4 +14,5 @@ | test.cpp:93:15:93:41 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | | test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | +| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | | test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp index 98755a5692e..5b3425029e9 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp @@ -158,10 +158,10 @@ void good_placement_new_with_exception_handling() { catch (...) { } } -int rand(); +int unknown_value_without_exceptions() noexcept; void may_throw() { - if(rand()) { + if(unknown_value_without_exceptions()) { throw "bad luck exception!"; } } @@ -170,11 +170,11 @@ void unknown_code_that_may_throw(int*); void unknown_code_that_will_not_throw(int*) noexcept; void calls_throwing_code(int* p) { - if(rand()) unknown_code_that_may_throw(p); + if(unknown_value_without_exceptions()) unknown_code_that_may_throw(p); } void calls_non_throwing(int* p) { - if (rand()) unknown_code_that_will_not_throw(p); + if (unknown_value_without_exceptions()) unknown_code_that_will_not_throw(p); } void good_new_with_throwing_call() { @@ -196,7 +196,7 @@ void good_new_with_throwing_call() { void bad_new_with_nonthrowing_call() { try { - int* p1 = new(std::nothrow) int; // BAD [NOT DETECTED] + int* p1 = new(std::nothrow) int; // BAD calls_non_throwing(p1); } catch(...) { } From 894f5d523c122890c9a00491e2e889cef6e0f18b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 7 May 2021 16:19:48 +0100 Subject: [PATCH 256/550] Update cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql Co-authored-by: Mathias Vorreiter Pedersen --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index b002cd3631e..4a872f29fd2 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -35,7 +35,7 @@ predicate isGuarded(SubExpr sub, Expr left, Expr right) { * `sub.getLeftOperand()`. */ predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { - n = DataFlow::exprNode(sub.getLeftOperand()) + n.asExpr() = sub.getLeftOperand() or exists(DataFlow::Node other | // dataflow From 5db6abe2f4979d03c9b92ed7e08756580ce7856d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 7 May 2021 16:22:48 +0100 Subject: [PATCH 257/550] Update cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql Co-authored-by: Mathias Vorreiter Pedersen --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 4a872f29fd2..d205824c9dd 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -61,7 +61,7 @@ predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { ) or exists(DataFlow::Node other, float p, float q | - // linear access of `e` + // linear access of `n` exprIsSubLeftOrLess(sub, other) and linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * e + q p >= 1 and From fc96c1c400b09e6772e94ce4b23d98136d3f52e1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 7 May 2021 16:26:23 +0100 Subject: [PATCH 258/550] Update cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql Co-authored-by: Mathias Vorreiter Pedersen --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index d205824c9dd..5448ae98dd4 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -49,7 +49,7 @@ predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { exists(DataFlow::Node other | // guard constraining `sub` exprIsSubLeftOrLess(sub, other) and - isGuarded(sub, other.asExpr(), n.asExpr()) // other >= e + isGuarded(sub, other.asExpr(), n.asExpr()) // other >= n ) or exists(DataFlow::Node other, float p, float q | From 91be483c57386846d22577dd1cc836f7eb81576d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 7 May 2021 16:26:36 +0100 Subject: [PATCH 259/550] Update cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql Co-authored-by: Mathias Vorreiter Pedersen --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 5448ae98dd4..daab33ff252 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -55,7 +55,7 @@ predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { exists(DataFlow::Node other, float p, float q | // linear access of `other` exprIsSubLeftOrLess(sub, other) and - linearAccess(n.asExpr(), other.asExpr(), p, q) and // e = p * other + q + linearAccess(n.asExpr(), other.asExpr(), p, q) and // n = p * other + q p <= 1 and q <= 0 ) From 69468514f00240c7a07fa3c640614cef9f5c488c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 7 May 2021 16:26:42 +0100 Subject: [PATCH 260/550] Update cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql Co-authored-by: Mathias Vorreiter Pedersen --- .../CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index daab33ff252..ff339af8b67 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -63,7 +63,7 @@ predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { exists(DataFlow::Node other, float p, float q | // linear access of `n` exprIsSubLeftOrLess(sub, other) and - linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * e + q + linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * n + q p >= 1 and q >= 0 ) From c2b96b3a5e8e8b0c7ff0a0f352e8e014233b29cf Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Fri, 7 May 2021 21:51:10 +0200 Subject: [PATCH 261/550] Add documentation to main classes' functions. Co-authored-by: Rasmus Wriedt Larsen --- python/ql/src/experimental/semmle/python/Concepts.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index cb90a465a54..18fd0b992eb 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -41,6 +41,9 @@ class LDAPQuery extends DataFlow::Node { LDAPQuery() { this = range } + /** + * Gets the argument containing the executed expression. + */ DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } } @@ -71,5 +74,8 @@ class LDAPEscape extends DataFlow::Node { LDAPEscape() { this = range } + /** + * Gets the argument containing the escaped expression. + */ DataFlow::Node getEscapeNode() { result = range.getEscapeNode() } } From 54b9f2175dfbf586fdaca3524d6f95ce6787d198 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 7 May 2021 16:02:54 -0400 Subject: [PATCH 262/550] C++: Allow annotating IR dumps with Alias Analysis info This commit adds a `PrintAliasAnalysis.qll` module, which can be imported alongside `PrintIR.qll` to annotate those dumps with alias analysis results. --- config/identical-files.json | 4 ++ .../aliased_ssa/internal/AliasAnalysis.qll | 43 +++++++++++++++++++ .../internal/PrintAliasAnalysis.qll | 19 ++++++++ .../unaliased_ssa/internal/AliasAnalysis.qll | 43 +++++++++++++++++++ .../internal/PrintAliasAnalysis.qll | 19 ++++++++ .../unaliased_ssa/internal/AliasAnalysis.qll | 43 +++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll diff --git a/config/identical-files.json b/config/identical-files.json index 3916e95a0cf..a17079e8ac0 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -249,6 +249,10 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll", "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll" ], + "SSA PrintAliasAnalysis": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll" + ], "C++ SSA AliasAnalysisImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll" diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..216fa72bf0d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -400,3 +400,46 @@ predicate addressOperandAllocationAndOffset( ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll new file mode 100644 index 00000000000..262088245e8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll @@ -0,0 +1,19 @@ +/** + * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`. + */ + +private import AliasAnalysisInternal +private import InputIR +private import AliasAnalysisImports +private import AliasAnalysis +private import semmle.code.cpp.ir.internal.IntegerConstant + +private class AliasPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + result = Print::getOperandProperty(operand, key) + } + + override string getInstructionProperty(Instruction instr, string key) { + result = Print::getInstructionProperty(instr, key) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..216fa72bf0d 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -400,3 +400,46 @@ predicate addressOperandAllocationAndOffset( ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll new file mode 100644 index 00000000000..262088245e8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll @@ -0,0 +1,19 @@ +/** + * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`. + */ + +private import AliasAnalysisInternal +private import InputIR +private import AliasAnalysisImports +private import AliasAnalysis +private import semmle.code.cpp.ir.internal.IntegerConstant + +private class AliasPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + result = Print::getOperandProperty(operand, key) + } + + override string getInstructionProperty(Instruction instr, string key) { + result = Print::getInstructionProperty(instr, key) + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index e26d61b9792..216fa72bf0d 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -400,3 +400,46 @@ predicate addressOperandAllocationAndOffset( ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} From 653ef9d257a4ded2701bc2da2548359ae0141ff5 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 7 May 2021 16:04:01 -0400 Subject: [PATCH 263/550] C++: Improve consistency failure message for multiple `MemoryLocation`s on a memory access. --- .../implementation/aliased_ssa/internal/SSAConstruction.qll | 5 ++++- .../unaliased_ssa/internal/SSAConstruction.qll | 5 ++++- .../unaliased_ssa/internal/SSAConstruction.qll | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 340f524fce8..39b3a7fbf5c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -934,7 +934,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 340f524fce8..39b3a7fbf5c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -934,7 +934,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 340f524fce8..39b3a7fbf5c 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -934,7 +934,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } From 34b8af30acd8d28b5f5980169ed0d0d4ec826455 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 7 May 2021 22:09:57 +0200 Subject: [PATCH 264/550] Move structure to LDAP.qll --- .../semmle/python/frameworks/LDAP.qll | 153 ++++++++++++++++++ .../semmle/python/frameworks/Stdlib.qll | 143 ---------------- 2 files changed, 153 insertions(+), 143 deletions(-) create mode 100644 python/ql/src/experimental/semmle/python/frameworks/LDAP.qll diff --git a/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll b/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll new file mode 100644 index 00000000000..2153f070457 --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll @@ -0,0 +1,153 @@ +/** + * Provides classes modeling security-relevant aspects of the LDAP libraries. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.dataflow.new.RemoteFlowSources +private import experimental.semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for Python's ldap-related libraries. + */ +private module LDAP { + /** + * Provides models for Python's `ldap` library. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html + */ + private module LDAP2 { + /** + * List of `ldap` methods used to execute a query. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions + */ + private class LDAP2QueryMethods extends string { + LDAP2QueryMethods() { + this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"] + } + } + + /** + * A class to find `ldap` methods executing a query. + * + * See `LDAP2QueryMethods` + */ + private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { + DataFlow::Node ldapNode; + + LDAP2Query() { + exists(DataFlow::AttrRead searchMethod | + this.getFunction() = searchMethod and + API::moduleImport("ldap").getMember("initialize").getACall() = + searchMethod.getObject().getALocalSource() and + searchMethod.getAttributeName() instanceof LDAP2QueryMethods and + ( + ldapNode = this.getArg(0) + or + ( + ldapNode = this.getArg(2) or + ldapNode = this.getArgByName("filterstr") + ) + ) + ) + } + + override DataFlow::Node getLDAPNode() { result = ldapNode } + } + + /** + * A class to find calls to `ldap.dn.escape_dn_chars`. + * + * See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17 + */ + private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP2EscapeDNCall() { + this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() + } + + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + } + + /** + * A class to find calls to `ldap.filter.escape_filter_chars`. + * + * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars + */ + private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP2EscapeFilterCall() { + this = + API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() + } + + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + } + } + + /** + * Provides models for Python's `ldap3` library. + * + * See https://pypi.org/project/ldap3/ + */ + private module LDAP3 { + /** + * A class to find `ldap3` methods executing a query. + */ + private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { + DataFlow::Node ldapNode; + + LDAP3Query() { + exists(DataFlow::AttrRead searchMethod | + this.getFunction() = searchMethod and + API::moduleImport("ldap3").getMember("Connection").getACall() = + searchMethod.getObject().getALocalSource() and + searchMethod.getAttributeName() = "search" and + ( + ldapNode = this.getArg(0) or + ldapNode = this.getArg(1) + ) + ) + } + + override DataFlow::Node getLDAPNode() { result = ldapNode } + } + + /** + * A class to find calls to `ldap3.utils.dn.escape_rdn`. + * + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390 + */ + private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP3EscapeDNCall() { + this = + API::moduleImport("ldap3") + .getMember("utils") + .getMember("dn") + .getMember("escape_rdn") + .getACall() + } + + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + } + + /** + * A class to find calls to `ldap3.utils.conv.escape_filter_chars`. + * + * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91 + */ + private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { + LDAP3EscapeFilterCall() { + this = + API::moduleImport("ldap3") + .getMember("utils") + .getMember("conv") + .getMember("escape_filter_chars") + .getACall() + } + + override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + } + } +} diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 2bbca55f820..420caf0d73b 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -9,146 +9,3 @@ private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.RemoteFlowSources private import experimental.semmle.python.Concepts private import semmle.python.ApiGraphs - -/** - * Provides models for Python's ldap-related libraries. - */ -private module LDAP { - /** - * Provides models for Python's `ldap` library. - * - * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html - */ - private module LDAP2 { - /** - * List of `ldap` methods used to execute a query. - * - * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions - */ - private class LDAP2QueryMethods extends string { - LDAP2QueryMethods() { - this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"] - } - } - - /** - * A class to find `ldap` methods executing a query. - * - * See `LDAP2QueryMethods` - */ - private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { - DataFlow::Node ldapNode; - - LDAP2Query() { - exists(DataFlow::AttrRead searchMethod | - this.getFunction() = searchMethod and - API::moduleImport("ldap").getMember("initialize").getACall() = - searchMethod.getObject().getALocalSource() and - searchMethod.getAttributeName() instanceof LDAP2QueryMethods and - ( - ldapNode = this.getArg(0) - or - ( - ldapNode = this.getArg(2) or - ldapNode = this.getArgByName("filterstr") - ) - ) - ) - } - - override DataFlow::Node getLDAPNode() { result = ldapNode } - } - - /** - * A class to find calls to `ldap.dn.escape_dn_chars`. - * - * See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17 - */ - private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { - LDAP2EscapeDNCall() { - this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() - } - - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } - } - - /** - * A class to find calls to `ldap.filter.escape_filter_chars`. - * - * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars - */ - private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { - LDAP2EscapeFilterCall() { - this = - API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() - } - - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } - } - } - - /** - * Provides models for Python's `ldap3` library. - * - * See https://pypi.org/project/ldap3/ - */ - private module LDAP3 { - /** - * A class to find `ldap3` methods executing a query. - */ - private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { - DataFlow::Node ldapNode; - - LDAP3Query() { - exists(DataFlow::AttrRead searchMethod | - this.getFunction() = searchMethod and - API::moduleImport("ldap3").getMember("Connection").getACall() = - searchMethod.getObject().getALocalSource() and - searchMethod.getAttributeName() = "search" and - ( - ldapNode = this.getArg(0) or - ldapNode = this.getArg(1) - ) - ) - } - - override DataFlow::Node getLDAPNode() { result = ldapNode } - } - - /** - * A class to find calls to `ldap3.utils.dn.escape_rdn`. - * - * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390 - */ - private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LDAPEscape::Range { - LDAP3EscapeDNCall() { - this = - API::moduleImport("ldap3") - .getMember("utils") - .getMember("dn") - .getMember("escape_rdn") - .getACall() - } - - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } - } - - /** - * A class to find calls to `ldap3.utils.conv.escape_filter_chars`. - * - * See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91 - */ - private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LDAPEscape::Range { - LDAP3EscapeFilterCall() { - this = - API::moduleImport("ldap3") - .getMember("utils") - .getMember("conv") - .getMember("escape_filter_chars") - .getACall() - } - - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } - } - } -} From 6159fbea2bc8f4fdacdcf76829f23c7124e44d38 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 7 May 2021 22:15:51 +0200 Subject: [PATCH 265/550] Update functions naming --- .../experimental/semmle/python/Concepts.qll | 8 +++--- .../semmle/python/frameworks/LDAP.qll | 26 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 18fd0b992eb..0d7c351974b 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -26,7 +26,7 @@ module LDAPQuery { /** * Gets the argument containing the executed expression. */ - abstract DataFlow::Node getLDAPNode(); + abstract DataFlow::Node getQuery(); } } @@ -44,7 +44,7 @@ class LDAPQuery extends DataFlow::Node { /** * Gets the argument containing the executed expression. */ - DataFlow::Node getLDAPNode() { result = range.getLDAPNode() } + DataFlow::Node getQuery() { result = range.getQuery() } } /** Provides classes for modeling LDAP components escape-related APIs. */ @@ -59,7 +59,7 @@ module LDAPEscape { /** * Gets the argument containing the escaped expression. */ - abstract DataFlow::Node getEscapeNode(); + abstract DataFlow::Node getAnInput(); } } @@ -77,5 +77,5 @@ class LDAPEscape extends DataFlow::Node { /** * Gets the argument containing the escaped expression. */ - DataFlow::Node getEscapeNode() { result = range.getEscapeNode() } + DataFlow::Node getAnInput() { result = range.getAnInput() } } diff --git a/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll b/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll index 2153f070457..6c3e03cb267 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/LDAP.qll @@ -36,7 +36,7 @@ private module LDAP { * See `LDAP2QueryMethods` */ private class LDAP2Query extends DataFlow::CallCfgNode, LDAPQuery::Range { - DataFlow::Node ldapNode; + DataFlow::Node ldapQuery; LDAP2Query() { exists(DataFlow::AttrRead searchMethod | @@ -45,17 +45,17 @@ private module LDAP { searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() instanceof LDAP2QueryMethods and ( - ldapNode = this.getArg(0) + ldapQuery = this.getArg(0) or ( - ldapNode = this.getArg(2) or - ldapNode = this.getArgByName("filterstr") + ldapQuery = this.getArg(2) or + ldapQuery = this.getArgByName("filterstr") ) ) ) } - override DataFlow::Node getLDAPNode() { result = ldapNode } + override DataFlow::Node getQuery() { result = ldapQuery } } /** @@ -68,7 +68,7 @@ private module LDAP { this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall() } - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result = this.getArg(0) } } /** @@ -82,7 +82,7 @@ private module LDAP { API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall() } - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result = this.getArg(0) } } } @@ -96,7 +96,7 @@ private module LDAP { * A class to find `ldap3` methods executing a query. */ private class LDAP3Query extends DataFlow::CallCfgNode, LDAPQuery::Range { - DataFlow::Node ldapNode; + DataFlow::Node ldapQuery; LDAP3Query() { exists(DataFlow::AttrRead searchMethod | @@ -105,13 +105,13 @@ private module LDAP { searchMethod.getObject().getALocalSource() and searchMethod.getAttributeName() = "search" and ( - ldapNode = this.getArg(0) or - ldapNode = this.getArg(1) + ldapQuery = this.getArg(0) or + ldapQuery = this.getArg(1) ) ) } - override DataFlow::Node getLDAPNode() { result = ldapNode } + override DataFlow::Node getQuery() { result = ldapQuery } } /** @@ -129,7 +129,7 @@ private module LDAP { .getACall() } - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result = this.getArg(0) } } /** @@ -147,7 +147,7 @@ private module LDAP { .getACall() } - override DataFlow::Node getEscapeNode() { result = this.getArg(0) } + override DataFlow::Node getAnInput() { result = this.getArg(0) } } } } From 2ad72ad69390652dc7e68b831e71ecaf020bc14f Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 7 May 2021 22:16:12 +0200 Subject: [PATCH 266/550] Add LDAP framework entry in Frameworks.qll --- python/ql/src/experimental/semmle/python/Frameworks.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/python/ql/src/experimental/semmle/python/Frameworks.qll b/python/ql/src/experimental/semmle/python/Frameworks.qll index ca1dd04e57d..b64532dda7c 100644 --- a/python/ql/src/experimental/semmle/python/Frameworks.qll +++ b/python/ql/src/experimental/semmle/python/Frameworks.qll @@ -3,3 +3,4 @@ */ private import experimental.semmle.python.frameworks.Stdlib +private import experimental.semmle.python.frameworks.LDAP \ No newline at end of file From f0a994a570ddd10937a70b7a9a1d9feca33c9d84 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 7 May 2021 16:33:15 -0400 Subject: [PATCH 267/550] C++: Fix pointer flow modeling for smart pointer setters --- .../code/cpp/models/implementations/SmartPointer.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll index 7f9639f6373..ab44671f83d 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -158,11 +158,11 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side // parameter. result.isParameter(1) else result.isParameterDeref(0) + or + // One of the functions that takes ownership of a raw pointer. + param0.getUnspecifiedType() instanceof PointerType and + result.isParameter(0) ) - or - // One of the functions that takes ownership of a raw pointer. - param0.getUnspecifiedType() instanceof PointerType and - result.isParameter(0) ) } } From 187e136ecc7d81d48b83f86804d116f549d53aa6 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 7 May 2021 16:50:03 -0400 Subject: [PATCH 268/550] C++: Generate IR side effects for smart pointer indirections When inserting side effect instructions for argument indirections, we now insert side effects for smart pointers as we would for raw pointers. The address operand of the side effect instruction is the smart pointer object, which is a bit odd. However, I'd like to think through the design of a more principled solution before doing additional work. A few new tests are added to the existing IR tests. In addition, the IR tests now `#include` some of the shared STL headers. I've disabled IR dumps for functions from those headers, since they only get in the way of the test cases we intended. --- .../raw/internal/SideEffects.qll | 4 +- .../library-tests/ir/ir/PrintAST.expected | 80 +++++++++++++++++++ cpp/ql/test/library-tests/ir/ir/PrintAST.ql | 11 +++ .../test/library-tests/ir/ir/PrintAST.qlref | 1 - .../test/library-tests/ir/ir/PrintConfig.qll | 10 +++ .../ir/ir/raw_consistency.expected | 1 + .../test/library-tests/ir/ir/raw_ir.expected | 74 +++++++++++++++++ cpp/ql/test/library-tests/ir/ir/raw_ir.ql | 11 +++ cpp/ql/test/library-tests/ir/ir/raw_ir.qlref | 1 - cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp | 20 +++++ 10 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 cpp/ql/test/library-tests/ir/ir/PrintAST.ql delete mode 100644 cpp/ql/test/library-tests/ir/ir/PrintAST.qlref create mode 100644 cpp/ql/test/library-tests/ir/ir/PrintConfig.qll create mode 100644 cpp/ql/test/library-tests/ir/ir/raw_ir.ql delete mode 100644 cpp/ql/test/library-tests/ir/ir/raw_ir.qlref create mode 100644 cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll index 350127a58d1..e7de9d26326 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -7,6 +7,7 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.models.interfaces.PointerWrapper private import semmle.code.cpp.models.interfaces.SideEffect /** @@ -39,7 +40,8 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff exists(Type t | t = expr.getUnspecifiedType() | t instanceof ArrayType or t instanceof PointerType or - t instanceof ReferenceType + t instanceof ReferenceType or + t instanceof PointerWrapper ) and ( isWrite = true and diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 44d379e46fe..4b997627182 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -11365,6 +11365,86 @@ perf-regression.cpp: # 12| Type = [IntType] int # 12| Value = [Literal] 0 # 12| ValueCategory = prvalue +smart_ptr.cpp: +# 8| [TopLevelFunction] void unique_ptr_arg(std::unique_ptr>) +# 8| : +# 8| getParameter(0): [Parameter] up +# 8| Type = [ClassTemplateInstantiation] unique_ptr> +# 10| [TopLevelFunction] void call_unique_ptr_arg(int*) +# 10| : +# 10| getParameter(0): [Parameter] p +# 10| Type = [IntPointerType] int * +# 10| getEntryPoint(): [BlockStmt] { ... } +# 11| getStmt(0): [DeclStmt] declaration +# 11| getDeclarationEntry(0): [VariableDeclarationEntry] definition of up +# 11| Type = [ClassTemplateInstantiation] unique_ptr> +# 11| getVariable().getInitializer(): [Initializer] initializer for up +# 11| getExpr(): [ConstructorCall] call to unique_ptr +# 11| Type = [VoidType] void +# 11| ValueCategory = prvalue +# 11| getArgument(0): [VariableAccess] p +# 11| Type = [IntPointerType] int * +# 11| ValueCategory = prvalue(load) +# 12| getStmt(1): [ExprStmt] ExprStmt +# 12| getExpr(): [FunctionCall] call to unique_ptr_arg +# 12| Type = [VoidType] void +# 12| ValueCategory = prvalue +# 12| getArgument(0): [FunctionCall] call to move +# 12| Type = [RValueReferenceType] type && +# 12| ValueCategory = prvalue +# 12| getArgument(0): [VariableAccess] up +# 12| Type = [ClassTemplateInstantiation] unique_ptr> +# 12| ValueCategory = lvalue +# 12| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 12| Type = [LValueReferenceType] unique_ptr> & +# 12| ValueCategory = prvalue +# 12| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 12| Type = [ClassTemplateInstantiation] unique_ptr> +# 12| ValueCategory = lvalue +# 12| getExpr(): [ReferenceDereferenceExpr] (reference dereference) +# 12| Type = [CTypedefType,NestedTypedefType] type +# 12| ValueCategory = prvalue(load) +# 13| getStmt(2): [ReturnStmt] return ... +# 15| [TopLevelFunction] void shared_ptr_arg(std::shared_ptr) +# 15| : +# 15| getParameter(0): [Parameter] sp +# 15| Type = [ClassTemplateInstantiation] shared_ptr +# 17| [TopLevelFunction] void call_shared_ptr_arg(float*) +# 17| : +# 17| getParameter(0): [Parameter] p +# 17| Type = [PointerType] float * +# 17| getEntryPoint(): [BlockStmt] { ... } +# 18| getStmt(0): [DeclStmt] declaration +# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp +# 18| Type = [ClassTemplateInstantiation] shared_ptr +# 18| getVariable().getInitializer(): [Initializer] initializer for sp +# 18| getExpr(): [ConstructorCall] call to shared_ptr +# 18| Type = [VoidType] void +# 18| ValueCategory = prvalue +# 18| getArgument(0): [VariableAccess] p +# 18| Type = [PointerType] float * +# 18| ValueCategory = prvalue(load) +# 19| getStmt(1): [ExprStmt] ExprStmt +# 19| getExpr(): [FunctionCall] call to shared_ptr_arg +# 19| Type = [VoidType] void +# 19| ValueCategory = prvalue +# 19| getArgument(0): [ConstructorCall] call to shared_ptr +# 19| Type = [VoidType] void +# 19| ValueCategory = prvalue +# 19| getArgument(0): [VariableAccess] sp +# 19| Type = [ClassTemplateInstantiation] shared_ptr +# 19| ValueCategory = lvalue +# 19| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 19| Type = [LValueReferenceType] const shared_ptr & +# 19| ValueCategory = prvalue +# 19| getExpr(): [CStyleCast] (const shared_ptr)... +# 19| Conversion = [GlvalueConversion] glvalue conversion +# 19| Type = [SpecifiedType] const shared_ptr +# 19| ValueCategory = lvalue +# 19| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 19| Type = [ClassTemplateInstantiation] shared_ptr +# 19| ValueCategory = lvalue +# 20| getStmt(2): [ReturnStmt] return ... struct_init.cpp: # 1| [TopLevelFunction] int handler1(void*) # 1| : diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.ql b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql new file mode 100644 index 00000000000..e107c80de02 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.PrintAST +private import PrintConfig + +private class PrintConfig extends PrintASTConfiguration { + override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } +} diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref b/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref deleted file mode 100644 index 6fcb30ac7a6..00000000000 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/cpp/PrintAST.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll new file mode 100644 index 00000000000..3253a1196b6 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -0,0 +1,10 @@ +private import cpp + +/** + * Holds if the AST or IR for the specified function should be printed in the test output. + * + * This predicate excludes functions defined in standard headers. + */ +predicate shouldDumpFunction(Function func) { + not func.getLocation().getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+") +} diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 31e5b01229c..57f16b48a1a 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -6,6 +6,7 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor +| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr>::~unique_ptr() | void std::unique_ptr>::~unique_ptr() | ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index e008e2de659..6bcfd2c43e4 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -7903,6 +7903,80 @@ perf-regression.cpp: # 9| v9_6(void) = AliasedUse : ~m? # 9| v9_7(void) = ExitFunction : +smart_ptr.cpp: +# 10| void call_unique_ptr_arg(int*) +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| mu10_2(unknown) = AliasedDefinition : +# 10| mu10_3(unknown) = InitializeNonLocal : +# 10| r10_4(glval) = VariableAddress[p] : +# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4 +# 10| r10_6(int *) = Load[p] : &:r10_4, ~m? +# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6 +# 11| r11_1(glval>>) = VariableAddress[up] : +# 11| mu11_2(unique_ptr>) = Uninitialized[up] : &:r11_1 +# 11| r11_3(glval) = FunctionAddress[unique_ptr] : +# 11| r11_4(glval) = VariableAddress[p] : +# 11| r11_5(int *) = Load[p] : &:r11_4, ~m? +# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5 +# 11| mu11_7(unknown) = ^CallSideEffect : ~m? +# 11| mu11_8(unique_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1 +# 12| r12_1(glval) = FunctionAddress[unique_ptr_arg] : +# 12| r12_2(glval>>) = VariableAddress[#temp12:20] : +# 12| r12_3(glval) = FunctionAddress[move] : +# 12| r12_4(glval>>) = VariableAddress[up] : +# 12| r12_5(unique_ptr> &) = CopyValue : r12_4 +# 12| r12_6(unique_ptr> &&) = Call[move] : func:r12_3, 0:r12_5 +# 12| r12_7(unique_ptr>) = Load[?] : &:r12_6, ~m? +# 12| mu12_8(unique_ptr>) = Store[#temp12:20] : &:r12_2, r12_7 +# 12| r12_9(unique_ptr>) = Load[#temp12:20] : &:r12_2, ~m? +# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9 +# 12| mu12_11(unknown) = ^CallSideEffect : ~m? +# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m? +# 13| v13_1(void) = NoOp : +# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m? +# 10| v10_9(void) = ReturnVoid : +# 10| v10_10(void) = AliasedUse : ~m? +# 10| v10_11(void) = ExitFunction : + +# 17| void call_shared_ptr_arg(float*) +# 17| Block 0 +# 17| v17_1(void) = EnterFunction : +# 17| mu17_2(unknown) = AliasedDefinition : +# 17| mu17_3(unknown) = InitializeNonLocal : +# 17| r17_4(glval) = VariableAddress[p] : +# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4 +# 17| r17_6(float *) = Load[p] : &:r17_4, ~m? +# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6 +# 18| r18_1(glval>) = VariableAddress[sp] : +# 18| mu18_2(shared_ptr) = Uninitialized[sp] : &:r18_1 +# 18| r18_3(glval) = FunctionAddress[shared_ptr] : +# 18| r18_4(glval) = VariableAddress[p] : +# 18| r18_5(float *) = Load[p] : &:r18_4, ~m? +# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5 +# 18| mu18_7(unknown) = ^CallSideEffect : ~m? +# 18| mu18_8(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r18_1 +# 19| r19_1(glval) = FunctionAddress[shared_ptr_arg] : +# 19| r19_2(glval>) = VariableAddress[#temp19:20] : +# 19| mu19_3(shared_ptr) = Uninitialized[#temp19:20] : &:r19_2 +# 19| r19_4(glval) = FunctionAddress[shared_ptr] : +# 19| r19_5(glval>) = VariableAddress[sp] : +# 19| r19_6(glval>) = Convert : r19_5 +# 19| r19_7(shared_ptr &) = CopyValue : r19_6 +# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7 +# 19| mu19_9(unknown) = ^CallSideEffect : ~m? +# 19| mu19_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r19_2 +# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m? +# 19| r19_12(shared_ptr) = Load[#temp19:20] : &:r19_2, ~m? +# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12 +# 19| mu19_14(unknown) = ^CallSideEffect : ~m? +# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m? +# 20| v20_1(void) = NoOp : +# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m? +# 17| v17_9(void) = ReturnVoid : +# 17| v17_10(void) = AliasedUse : ~m? +# 17| v17_11(void) = ExitFunction : + struct_init.cpp: # 16| void let_info_escape(Info*) # 16| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql new file mode 100644 index 00000000000..a0ebe4d2bdd --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.ir.implementation.raw.PrintIR +private import PrintConfig + +private class PrintConfig extends PrintIRConfiguration { + override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } +} diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref b/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref deleted file mode 100644 index fa4c4bda903..00000000000 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/cpp/ir/implementation/raw/PrintIR.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp new file mode 100644 index 00000000000..980c7767009 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp @@ -0,0 +1,20 @@ +#include "../../../include/memory.h" +#include "../../../include/utility.h" + +using std::move; +using std::shared_ptr; +using std::unique_ptr; + +void unique_ptr_arg(unique_ptr up); + +void call_unique_ptr_arg(int* p) { + unique_ptr up(p); + unique_ptr_arg(move(up)); +} + +void shared_ptr_arg(shared_ptr sp); + +void call_shared_ptr_arg(float* p) { + shared_ptr sp(p); + shared_ptr_arg(sp); +} From fd88b7210101f75af27b938878f9b6ad1abddae7 Mon Sep 17 00:00:00 2001 From: Hayk Andriasyan <77549590+p0wn4j@users.noreply.github.com> Date: Sat, 8 May 2021 12:51:15 +0400 Subject: [PATCH 269/550] Delete JSchOSInjection.qhelp --- .../CWE/CWE-078/JSchOSInjection.qhelp | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp deleted file mode 100644 index 57e8a61fe74..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qhelp +++ /dev/null @@ -1,64 +0,0 @@ - - - - -

    -JSch is a pure Java implementation of SSH2. -JSch allows you to connect to a sshd server and use port forwarding, X11 forwarding, -file transfer, command execution, etc., and you can integrate its functionality into your own Java programs. -JSch is licensed under BSD style license. -If an OS command is built using an attacker-controlled data, it may allow the attacker -to run an arbitrary code on the remote host. -

    -
    - - -

    -Including an user input in a JSch lib's OS command expression should be avoided. -If this is not possible, then a strong input validation must be performed. -Some examples of an effective validation include: - -Validating against an allowlist of permitted values. -Validating that the input is a number. -Validating that the input contains only alphanumeric characters, no other syntax or whitespace. - -Never attempt to sanitize input by escaping shell metacharacters. In practice, -this is just too error-prone and vulnerable to being bypassed by a skilled attacker. - -

    -
    - - -

    -The following example uses the untrusted data to build an OS command. -

    - -
    - - -

    -The following example validates the untrusted data before build an OS command. -

    - -
    - - -
  • - JCraft: - JSch - Java Secure Channel. -
  • -
  • - OWASP: - Command Injection. -
  • -
  • - OWASP: - OS Command Injection Defense Cheat Sheet. -
  • -
  • - PortSwigger: - OS command injection. -
  • - -
    -
    \ No newline at end of file From d9f243d18a487736c54d9f5655116fb4222fcbb0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sat, 8 May 2021 11:14:02 -0400 Subject: [PATCH 270/550] Java: Fix QLDoc for `Container.toString()` Fixes #5828 The QLDoc was just too specific about the default implementation. I've improved the wording. --- java/ql/src/semmle/code/FileSystem.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/FileSystem.qll b/java/ql/src/semmle/code/FileSystem.qll index 13908d547dc..708348b85f5 100755 --- a/java/ql/src/semmle/code/FileSystem.qll +++ b/java/ql/src/semmle/code/FileSystem.qll @@ -146,9 +146,11 @@ class Container extends @container, Top { } /** - * Gets a textual representation of the path of this container. + * Gets a textual representation of this container. * - * This is the absolute path of the container. + * The default implementation returns the absolute path to the container, but subclasses may + * may override to provide a different result. To get the absolute path for any `Container`, call + * `Container.getAbsolutePath()` directly. */ override string toString() { result = getAbsolutePath() } } From 8665747316064806f09a0018f48c7ce439bf7ee6 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Sat, 8 May 2021 18:08:50 +0200 Subject: [PATCH 271/550] Update sink and sanitizer to match new naming --- .../experimental/semmle/python/security/injection/LDAP.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll b/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll index 9f91f91b321..1927e7a95d3 100644 --- a/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll +++ b/python/ql/src/experimental/semmle/python/security/injection/LDAP.qll @@ -16,9 +16,9 @@ class LDAPInjectionFlowConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery ldapQuery).getLDAPNode() } + override predicate isSink(DataFlow::Node sink) { sink = any(LDAPQuery ldapQuery).getQuery() } override predicate isSanitizer(DataFlow::Node sanitizer) { - sanitizer = any(LDAPEscape ldapEsc).getEscapeNode() + sanitizer = any(LDAPEscape ldapEsc).getAnInput() } } From 3fe5dd0f3508525ba9ef9724784d15709dbc04d9 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 10:05:18 +0200 Subject: [PATCH 272/550] add comment about filtering away jQuery from the source --- .../security/dataflow/UnsafeHtmlConstructionCustomizations.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 8546041ef06..61ee6cd4622 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -25,6 +25,7 @@ module UnsafeHtmlConstruction { class ExternalInputSource extends Source, DataFlow::ParameterNode { ExternalInputSource() { this = Exports::getALibraryInputParameter() and + // An AMD-style module sometimes loads the jQuery library in a way which looks like library input. not this = JQuery::dollarSource() } } From 7ac783097329729dc3f4d1a38de689a867a5dd55 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 10:08:42 +0200 Subject: [PATCH 273/550] C++: Add testcase with false positive involving a conversion on the large-expression side of the comparison. --- .../ComparisonWithWiderType/ComparisonWithWiderType.expected | 1 + .../CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected index d04bff0a812..58e4a4acfed 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected @@ -1,3 +1,4 @@ +| test3.cpp:2:8:2:53 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test3.cpp:1:36:1:40 | small | small | test3.cpp:2:43:2:52 | ... - ... | ... - ... | | test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x | | test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x | | test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp new file mode 100644 index 00000000000..000987cdcfa --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp @@ -0,0 +1,3 @@ +void test_issue_5850(unsigned char small, unsigned int large1) { + for(; small < static_cast(large1 - 1); small++) { } // GOOD [FALSE POSITIVE] +} \ No newline at end of file From c91ed80e6ca5d8aa83a8207c50778b6a5fadf287 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 10:12:43 +0200 Subject: [PATCH 274/550] C++: Fix false positive by computing range of the converted expression. --- cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql | 2 +- .../ComparisonWithWiderType/ComparisonWithWiderType.expected | 1 - .../CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 3303316cede..8c33f78316a 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -49,7 +49,7 @@ where small = rel.getLesserOperand() and large = rel.getGreaterOperand() and rel = l.getCondition().getAChild*() and - upperBound(large).log2() > getComparisonSize(small) * 8 and + upperBound(large.getFullyConverted()).log2() > getComparisonSize(small) * 8 and // Ignore cases where the smaller type is int or larger // These are still bugs, but you should need a very large string or array to // trigger them. We will want to disable this for some applications, but it's diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected index 58e4a4acfed..d04bff0a812 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected @@ -1,4 +1,3 @@ -| test3.cpp:2:8:2:53 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test3.cpp:1:36:1:40 | small | small | test3.cpp:2:43:2:52 | ... - ... | ... - ... | | test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x | | test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x | | test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp index 000987cdcfa..3ae02d9d56c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp @@ -1,3 +1,3 @@ void test_issue_5850(unsigned char small, unsigned int large1) { - for(; small < static_cast(large1 - 1); small++) { } // GOOD [FALSE POSITIVE] + for(; small < static_cast(large1 - 1); small++) { } // GOOD } \ No newline at end of file From 474b337eeb2697b9534ee0432dd29a4dc8ae0c92 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 10:22:44 +0200 Subject: [PATCH 275/550] C++: Add change-note. --- cpp/change-notes/2021-10-05-comparison-with-wider-type.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-10-05-comparison-with-wider-type.md diff --git a/cpp/change-notes/2021-10-05-comparison-with-wider-type.md b/cpp/change-notes/2021-10-05-comparison-with-wider-type.md new file mode 100644 index 00000000000..f06895a3c1a --- /dev/null +++ b/cpp/change-notes/2021-10-05-comparison-with-wider-type.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Comparison with wider type' (cpp/comparison-with-wider-type) query has been improved to produce fewer false positives. \ No newline at end of file From df5eab33f946ed0b88da4287f24659e01ab6e399 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 10 May 2021 09:43:33 +0100 Subject: [PATCH 276/550] JS: Update relevantTaintSource() --- javascript/ql/src/meta/internal/TaintMetrics.qll | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/javascript/ql/src/meta/internal/TaintMetrics.qll b/javascript/ql/src/meta/internal/TaintMetrics.qll index 6d10b2c6ad6..f6eae2eaa6e 100644 --- a/javascript/ql/src/meta/internal/TaintMetrics.qll +++ b/javascript/ql/src/meta/internal/TaintMetrics.qll @@ -75,16 +75,9 @@ DataFlow::Node relevantTaintSink(string kind) { DataFlow::Node relevantTaintSink() { result = relevantTaintSink(_) } /** - * Gets a remote flow source or `document.location` source. + * Gets a relevant remote flow source. */ -DataFlow::Node relevantTaintSource() { - not result.getFile() instanceof IgnoredFile and - ( - result instanceof RemoteFlowSource - or - result = DOM::locationSource() - ) -} +RemoteFlowSource relevantTaintSource() { not result.getFile() instanceof IgnoredFile } /** * Gets the output of a call that shows intent to sanitize a value From 646bf994896d951c90a2cc09dc40ef1500625a63 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 10:45:33 +0200 Subject: [PATCH 277/550] rewrite the qhelp to focus more on documenting unsafe functions --- .../CWE-079/UnsafeHtmlConstruction.qhelp | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp index 619b55e4920..0bc3bd53a0d 100644 --- a/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp +++ b/javascript/ql/src/Security/CWE-079/UnsafeHtmlConstruction.qhelp @@ -4,26 +4,22 @@

    - Dynamically constructing HTML with inputs from library functions that are - available to external clients may inadvertently leave a client open to XSS attacks. - - Clients using the exported function may use inputs containing unsafe HTML, - and if these inputs end up in the DOM, the client may be vulnerable to - cross-site scripting attacks. -

    + When a library function dynamically constructs HTML in a potentially unsafe + way, then it's important to document to clients of the library that the function + should only be used with trusted inputs. + If the function is not documented as being potentially unsafe, then a client + may inadvertently use inputs containing unsafe HTML fragments, and thereby leave + the client vulnerable to cross-site scripting attacks. +

    - If possible, use safe APIs when inserting HTML into the DOM. - Such as writing to the innerText property instead of innerHTML. + Document all library functions that can lead to cross-site scripting + attacks, and guard against unsafe inputs where dynamic HTML + construction is not intended.

    - -

    - Alternatively, use a HTML sanitizer to escape/remove unsafe content. -

    -
    @@ -41,13 +37,14 @@

    - To avoid such attacks, a program can use safe APIs such as innerText. + The library could either document that this function should not be used + with unsafe inputs, or use safe APIs such as innerText.

    - Alternatively, use a HTML sanitizer to remove unsafe content. + Alternatively, a HTML sanitizer can be used to remove unsafe content.

    From b4e35f54d9bc6900f2f881e05366a7dc043b92d9 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 10:47:34 +0200 Subject: [PATCH 278/550] fix typo --- javascript/ql/src/semmle/javascript/frameworks/Markdown.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll index 1a6c9d7a8c2..b2235577b04 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Markdown.qll @@ -10,7 +10,7 @@ import javascript */ module Markdown { /** - * A taint-step that parses a markdown document, but preserves taint.import + * A taint-step that parses a markdown document, but preserves taint. */ class MarkdownStep extends Unit { /** From 7ed20a8b2c07be79f5ba5936d52870fd2c21940f Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 10:55:21 +0200 Subject: [PATCH 279/550] Python: Add reminder to update docs for new frameworks --- python/ql/src/semmle/python/Frameworks.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll index a00511ca545..1c8ef1f551b 100644 --- a/python/ql/src/semmle/python/Frameworks.qll +++ b/python/ql/src/semmle/python/Frameworks.qll @@ -2,6 +2,8 @@ * Helper file that imports all framework modeling. */ +// If you add modeling of a new framework/library, remember to add it it to the docs in +// `docs/codeql/support/reusables/frameworks.rst` private import semmle.python.frameworks.Cryptodome private import semmle.python.frameworks.Cryptography private import semmle.python.frameworks.Dill From d9136689430f412491f81830bc06801f7b3a40f6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 10:55:09 +0200 Subject: [PATCH 280/550] move hasPathWithoutUnmatchedReturn to Configuration.qll --- .../semmle/javascript/dataflow/Configuration.qll | 11 +++++++++++ .../security/dataflow/UnsafeHtmlConstruction.qll | 2 +- .../UnsafeHtmlConstructionCustomizations.qll | 13 ------------- .../dataflow/UnsafeShellCommandConstruction.qll | 3 +-- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll index b4842026e24..45e7515df91 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Configuration.qll @@ -2068,3 +2068,14 @@ class VarAccessBarrier extends DataFlow::Node { ) } } + +/** + * Holds if there is a path without unmatched return steps from `source` to `sink`. + */ +predicate hasPathWithoutUnmatchedReturn(SourcePathNode source, SinkPathNode sink) { + exists(MidPathNode mid | + source.getASuccessor*() = mid and + sink = mid.getASuccessor() and + mid.getPathSummary().hasReturn() = false + ) +} diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll index 53debb48ff6..4fe675b16d9 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstruction.qll @@ -38,7 +38,7 @@ module UnsafeHtmlConstruction { // override to require that there is a path without unmatched return steps override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { super.hasFlowPath(source, sink) and - hasPathWithoutUnmatchedReturn(source, sink) + DataFlow::hasPathWithoutUnmatchedReturn(source, sink) } override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index 61ee6cd4622..c660657a322 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -174,17 +174,4 @@ module UnsafeHtmlConstruction { override string describe() { result = "Markdown rendering" } } - - /** - * Holds if there is a path without unmatched return steps from `source` to `sink`. - */ - predicate hasPathWithoutUnmatchedReturn( - DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink - ) { - exists(DataFlow::MidPathNode mid | - source.getASuccessor*() = mid and - sink = mid.getASuccessor() and - mid.getPathSummary().hasReturn() = false - ) - } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll index 2c7de7555de..d47b3696de6 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstruction.qll @@ -14,7 +14,6 @@ import javascript */ module UnsafeShellCommandConstruction { import UnsafeShellCommandConstructionCustomizations::UnsafeShellCommandConstruction - import UnsafeHtmlConstructionCustomizations /** * A taint-tracking configuration for reasoning about shell command constructed from library input vulnerabilities. @@ -36,7 +35,7 @@ module UnsafeShellCommandConstruction { // override to require that there is a path without unmatched return steps override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) { super.hasFlowPath(source, sink) and - UnsafeHtmlConstruction::requireMatchedReturn(source, sink) + DataFlow::hasPathWithoutUnmatchedReturn(source, sink) } override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { From f4e636dcd628bc00bcf367dadee993d156a164e6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 10 May 2021 10:08:10 +0100 Subject: [PATCH 281/550] Update javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll Co-authored-by: Esben Sparre Andreasen --- .../ql/src/semmle/javascript/frameworks/ClassValidator.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll b/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll index f0e95d0583d..381451c393c 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClassValidator.qll @@ -9,7 +9,7 @@ import javascript */ module ClassValidator { /** - * Holds if the decorator with the given name does not sanitize the input, for the purpose of taint tracking. + * Holds if the decorator with the given name sanitizes the input, for the purpose of taint tracking. */ bindingset[name] private predicate isSanitizingDecoratorName(string name) { From 8afdf2654035bcc38eb4c343572f94d0b7d4560d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 10:57:33 +0200 Subject: [PATCH 282/550] Python: Add modeling of idna PyPI package --- docs/codeql/support/reusables/frameworks.rst | 1 + .../2021-05-10-idna-add-modeling.md | 2 + python/ql/src/semmle/python/Frameworks.qll | 1 + .../ql/src/semmle/python/frameworks/Idna.qll | 40 +++++++++++++++++++ .../frameworks/idna/ConceptsTest.expected | 0 .../frameworks/idna/ConceptsTest.ql | 2 + .../frameworks/idna/InlineTaintTest.expected | 3 ++ .../frameworks/idna/InlineTaintTest.ql | 1 + .../frameworks/idna/taint_test.py | 13 ++++++ 9 files changed, 63 insertions(+) create mode 100644 python/change-notes/2021-05-10-idna-add-modeling.md create mode 100644 python/ql/src/semmle/python/frameworks/Idna.qll create mode 100644 python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected create mode 100644 python/ql/test/library-tests/frameworks/idna/ConceptsTest.ql create mode 100644 python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected create mode 100644 python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql create mode 100644 python/ql/test/library-tests/frameworks/idna/taint_test.py diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index afdf63bdb7b..d54c07454bc 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -158,6 +158,7 @@ Python built-in support dill, Serialization fabric, Utility library invoke, Utility library + idna, Utility library mysql-connector-python, Database MySQLdb, Database psycopg2, Database diff --git a/python/change-notes/2021-05-10-idna-add-modeling.md b/python/change-notes/2021-05-10-idna-add-modeling.md new file mode 100644 index 00000000000..95856ffe5a8 --- /dev/null +++ b/python/change-notes/2021-05-10-idna-add-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added modeling of the PyPI package `idna`, for encoding/decoding Internationalised Domain Names in Applications. diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll index 1c8ef1f551b..ed8f308c70a 100644 --- a/python/ql/src/semmle/python/Frameworks.qll +++ b/python/ql/src/semmle/python/Frameworks.qll @@ -10,6 +10,7 @@ private import semmle.python.frameworks.Dill private import semmle.python.frameworks.Django private import semmle.python.frameworks.Fabric private import semmle.python.frameworks.Flask +private import semmle.python.frameworks.Idna private import semmle.python.frameworks.Invoke private import semmle.python.frameworks.MysqlConnectorPython private import semmle.python.frameworks.MySQLdb diff --git a/python/ql/src/semmle/python/frameworks/Idna.qll b/python/ql/src/semmle/python/frameworks/Idna.qll new file mode 100644 index 00000000000..331cafbd084 --- /dev/null +++ b/python/ql/src/semmle/python/frameworks/Idna.qll @@ -0,0 +1,40 @@ +/** + * Provides classes modeling security-relevant aspects of the `idna` PyPI package. + * See https://pypi.org/project/idna/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for the `idna` PyPI package. + * See https://pypi.org/project/idna/. + */ +private module IdnaModel { + /** A call to `idna.encode`. */ + private class IdnaEncodeCall extends Encoding::Range, DataFlow::CallCfgNode { + IdnaEncodeCall() { this = API::moduleImport("idna").getMember("encode").getACall() } + + override DataFlow::Node getAnInput() { result = [this.getArg(0), this.getArgByName("s")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "IDNA" } + } + + /** A call to `idna.decode`. */ + private class IdnaDecodeCall extends Decoding::Range, DataFlow::CallCfgNode { + IdnaDecodeCall() { this = API::moduleImport("idna").getMember("decode").getACall() } + + override DataFlow::Node getAnInput() { result = [this.getArg(0), this.getArgByName("s")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "IDNA" } + + override predicate mayExecuteInput() { none() } + } +} diff --git a/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/idna/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/idna/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/idna/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected new file mode 100644 index 00000000000..79d760d87f4 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected @@ -0,0 +1,3 @@ +argumentToEnsureNotTaintedNotMarkedAsSpurious +untaintedArgumentToEnsureTaintedNotMarkedAsMissing +failures diff --git a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql new file mode 100644 index 00000000000..027ad8667be --- /dev/null +++ b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql @@ -0,0 +1 @@ +import experimental.meta.InlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/idna/taint_test.py b/python/ql/test/library-tests/frameworks/idna/taint_test.py new file mode 100644 index 00000000000..61b9dad166c --- /dev/null +++ b/python/ql/test/library-tests/frameworks/idna/taint_test.py @@ -0,0 +1,13 @@ +import idna + +def test_idna(): + ts = TAINTED_STRING + tb = TAINTED_BYTES + + ensure_tainted( + idna.encode(ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA + idna.encode(s=ts), # $ tainted encodeInput=ts encodeOutput=Attribute() encodeFormat=IDNA + + idna.decode(tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA + idna.decode(s=tb), # $ tainted decodeInput=tb decodeOutput=Attribute() decodeFormat=IDNA + ) From 8f91e9eba0d50cb0dc142f35a78b922629dca64c Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Mon, 10 May 2021 10:30:26 +0100 Subject: [PATCH 283/550] JavaScript: Model chaining calls in sqlite3. --- .../change-notes/2021-05-10-sqlite3-chaining.md | 3 +++ .../ql/src/semmle/javascript/frameworks/SQL.qll | 14 ++++++++++++-- .../frameworks/SQL/SqlString.expected | 1 + .../ql/test/library-tests/frameworks/SQL/sqlite.js | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 javascript/change-notes/2021-05-10-sqlite3-chaining.md diff --git a/javascript/change-notes/2021-05-10-sqlite3-chaining.md b/javascript/change-notes/2021-05-10-sqlite3-chaining.md new file mode 100644 index 00000000000..2b541440308 --- /dev/null +++ b/javascript/change-notes/2021-05-10-sqlite3-chaining.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Modelling of chaining methods in the `sqlite3` package has improved, which may lead to + additional results from the `js/sql-injection` query. diff --git a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll index ce702decc96..4d345142eb4 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/SQL.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/SQL.qll @@ -341,18 +341,28 @@ private module Sqlite { result = sqlite().getMember("verbose").getReturn() } - /** Gets an expression that constructs a Sqlite database instance. */ + /** Gets an expression that constructs or returns a Sqlite database instance. */ API::Node database() { // new require('sqlite3').Database() result = sqlite().getMember("Database").getInstance() or + // chained call + result = getAChainingQueryCall() + or result = API::Node::ofType("sqlite3", "Database") } + /** A call to a query method on a Sqlite database instance that returns the same instance. */ + private API::Node getAChainingQueryCall() { + result = database().getMember(["all", "each", "exec", "get", "run"]).getReturn() + } + /** A call to a Sqlite query method. */ private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { QueryCall() { - this = database().getMember(["all", "each", "exec", "get", "prepare", "run"]).getACall() + this = getAChainingQueryCall().getAnImmediateUse() + or + this = database().getMember("prepare").getACall() } override DataFlow::Node getAQueryArgument() { result = getArgument(0) } diff --git a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected index 81338e00140..cf9470ce355 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected +++ b/javascript/ql/test/library-tests/frameworks/SQL/SqlString.expected @@ -66,5 +66,6 @@ | spannerImport.js:4:8:4:17 | "SQL code" | | sqlite-types.ts:4:12:4:49 | "UPDATE ... id = ?" | | sqlite.js:7:8:7:45 | "UPDATE ... id = ?" | +| sqlite.js:8:8:8:45 | "UPDATE ... id = ?" | | sqliteArray.js:6:12:6:49 | "UPDATE ... id = ?" | | sqliteImport.js:2:8:2:44 | "UPDATE ... id = ?" | diff --git a/javascript/ql/test/library-tests/frameworks/SQL/sqlite.js b/javascript/ql/test/library-tests/frameworks/SQL/sqlite.js index e2f072902d0..da03517c839 100644 --- a/javascript/ql/test/library-tests/frameworks/SQL/sqlite.js +++ b/javascript/ql/test/library-tests/frameworks/SQL/sqlite.js @@ -4,6 +4,7 @@ var sqlite = require('sqlite3'); var db = new sqlite.Database(":memory:"); -db.run("UPDATE tbl SET name = ? WHERE id = ?", "bar", 2); +db.run("UPDATE tbl SET name = ? WHERE id = ?", "bar", 2) + .run("UPDATE tbl SET name = ? WHERE id = ?", "foo", 3); exports.db = db; From dcdd54593e043d0ff889e1ddcb5dd556017d5219 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Mon, 10 May 2021 13:03:40 +0200 Subject: [PATCH 284/550] C++: Adjust user-defined literals test' expectations --- cpp/ql/test/library-tests/udl/udl.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/udl/udl.expected b/cpp/ql/test/library-tests/udl/udl.expected index fce69664a06..52c57beffc5 100644 --- a/cpp/ql/test/library-tests/udl/udl.expected +++ b/cpp/ql/test/library-tests/udl/udl.expected @@ -1,4 +1,4 @@ | udl.cpp:5:25:5:28 | Xy | udl.cpp:5:25:5:28 | initializer for xy | | udl.cpp:6:27:6:34 | XyXy | udl.cpp:6:27:6:34 | initializer for xyxy | -| udl.cpp:7:26:7:30 | X | udl.cpp:7:26:7:26 | call to operator "_Y | -| udl.cpp:8:29:8:38 | XX | udl.cpp:8:29:8:29 | call to operator "_Y | +| udl.cpp:7:26:7:30 | X | udl.cpp:7:26:7:26 | call to operator ""_Y | +| udl.cpp:8:29:8:38 | XX | udl.cpp:8:29:8:29 | call to operator ""_Y | From 7f1f2b4dd373f86c344f8963373dae6830e12921 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 10 May 2021 13:05:51 +0200 Subject: [PATCH 285/550] C#: Fix `GetHashCode/Equals` on `EscapingTextWriter` --- csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs index 5f6805de658..6294ec3ffd3 100644 --- a/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs +++ b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction /// HTML escapes the characters `&`, `{`, `}`, `"`, `@`, and `#`, before /// writing to the underlying object. ///
    - public class EscapingTextWriter : TextWriter + public sealed class EscapingTextWriter : TextWriter { private readonly TextWriter wrapped; private readonly bool disposeUnderlying; @@ -93,7 +93,7 @@ namespace Semmle.Extraction => throw new NotImplementedException(); public override bool Equals(object? obj) - => wrapped.Equals(obj); + => wrapped.Equals(obj) && obj is EscapingTextWriter other && disposeUnderlying == other.disposeUnderlying; public override void Flush() => wrapped.Flush(); @@ -102,7 +102,7 @@ namespace Semmle.Extraction => wrapped.FlushAsync(); public override int GetHashCode() - => wrapped.GetHashCode(); + => HashCode.Combine(wrapped, disposeUnderlying); public override string ToString() => wrapped.ToString() ?? ""; From c8f2937df9bd4d990f0cf654161319c668475e13 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:16:11 +0300 Subject: [PATCH 286/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.ql --- .../DeclarationOfVariableWithUnnecessarilyWideScope.ql | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql index be712b1cb1d..e73f36145c6 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -27,8 +27,6 @@ class DangerousWhileLoop extends WhileStmt { not exp instanceof PointerFieldAccess and not exp instanceof ValueFieldAccess and exp.(VariableAccess).getTarget().getName() = dl.getName() and - not exp.getParent*() instanceof CrementOperation and - not exp.getParent*() instanceof Assignment and not exp.getParent*() instanceof FunctionCall } @@ -37,10 +35,10 @@ class DangerousWhileLoop extends WhileStmt { /** Holds when there are changes to the variables involved in the condition. */ predicate isUseThisVariable() { exists(Variable v | - exp.(VariableAccess).getTarget() = v and + this.getCondition().getAChild*().(VariableAccess).getTarget() = v and ( exists(Assignment aexp | - aexp = this.getStmt().getAChild*() and + this = aexp.getEnclosingStmt().getParentStmt*() and ( aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v or @@ -49,7 +47,7 @@ class DangerousWhileLoop extends WhileStmt { ) or exists(CrementOperation crm | - crm = this.getStmt().getAChild*() and + this = crm.getEnclosingStmt().getParentStmt*() and crm.getOperand().(VariableAccess).getTarget() = v ) ) @@ -59,4 +57,4 @@ class DangerousWhileLoop extends WhileStmt { from DangerousWhileLoop lp where not lp.isUseThisVariable() -select lp.getDeclaration(), "A variable with this name is used in the loop condition." +select lp.getDeclaration(), "A variable with this name is used in the $@ condition.", lp, "loop" From d3c6093f37d14861a10cf1ef2134c8bc183340d7 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:16:38 +0300 Subject: [PATCH 287/550] Update test.c --- .../Security/CWE/CWE-1126/semmle/tests/test.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c index 325c278f697..47d89188e6b 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -1,5 +1,6 @@ void workFunction_0(char *s) { int intIndex = 10; + int intGuard; char buf[80]; while(intIndex > 2) // GOOD { @@ -14,6 +15,32 @@ void workFunction_0(char *s) { intIndex--; } intIndex = 10; + intGuard = 20; + while(intIndex < intGuard--) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex++; + intGuard--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + intGuard -= 4; + } + intIndex = 10; while(intIndex > 2) // GOOD { buf[intIndex] = 1; From 3e5dc1efb7ea527e8b2d7b8149ce2f5dbc76a2ef Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 10 May 2021 13:17:25 +0200 Subject: [PATCH 288/550] JS: More robust hasUnderlyingType --- javascript/ql/src/semmle/javascript/TypeScript.qll | 2 +- javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll | 1 - .../TypeScript/HasUnderlyingType/HasUnderlyingType.expected | 4 ++++ .../TypeScript/HasUnderlyingType/HasUnderlyingType.ql | 4 ++++ .../test/library-tests/TypeScript/HasUnderlyingType/foo.ts | 5 +++++ 5 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 4fe263c8389..0b611140449 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -725,7 +725,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { spec.getImportedName() = exportedName and this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess() or - spec instanceof ImportNamespaceSpecifier and + (spec instanceof ImportNamespaceSpecifier or spec instanceof ImportDefaultSpecifier) and this = spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName) ) diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index c73c894ca4b..f02e9b0f287 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -239,7 +239,6 @@ module DataFlow { private TypeAnnotation getFallbackTypeAnnotation() { exists(BindingPattern pattern | this = valueNode(pattern) and - not ast_node_type(pattern, _) and result = pattern.getTypeAnnotation() ) or diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected index 95c6d7e03d9..47e57e0a242 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected @@ -1,2 +1,6 @@ +underlyingTypeNode +| foo | Bar | foo.ts:3:1:5:1 | use (instance (member Bar (member exports (module foo)))) | +| foo | Bar | foo.ts:3:12:3:12 | use (instance (member Bar (member exports (module foo)))) | +#select | tst.ts:8:14:8:16 | arg | Base in global scope | | tst.ts:8:14:8:16 | arg | Sub in global scope | diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql index 77e311b3b76..72d4e6d0f3d 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql @@ -3,3 +3,7 @@ import javascript from Expr e, TypeName typeName where e.getType().hasUnderlyingTypeName(typeName) select e, typeName + +query API::Node underlyingTypeNode(string mod, string name) { + result = API::Node::ofType(mod, name) +} diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts new file mode 100644 index 00000000000..1b5be79068a --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts @@ -0,0 +1,5 @@ +import foo from "foo"; + +function f(x: foo.Bar) { + return x; +} From 9e5a38debd315a8f7ca90c9bcc78d7798b334e5d Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 10 May 2021 14:17:40 +0300 Subject: [PATCH 289/550] Update DeclarationOfVariableWithUnnecessarilyWideScope.expected --- .../DeclarationOfVariableWithUnnecessarilyWideScope.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected index 4b29dad8779..244a28cf332 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -1 +1 @@ -| test.c:13:9:13:16 | intIndex | A variable with this name is used in the loop condition. | +| test.c:14:9:14:16 | intIndex | A variable with this name is used in the $@ condition. | test.c:11:3:16:3 | while (...) ... | loop | From d6f9e37e3943f689891df1360c651c61c0a403e6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 13:31:00 +0200 Subject: [PATCH 290/550] add printAst.ql support for regular expressions --- .../ql/src/semmle/javascript/PrintAst.qll | 36 +++- .../ql/src/semmle/javascript/Regexp.qll | 100 +++++++++-- .../library-tests/TypeScript/Types/dummy.ts | 2 + .../TypeScript/Types/printAst.expected | 163 +++++++++++------- .../TypeScript/Types/tests.expected | 2 + 5 files changed, 219 insertions(+), 84 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 0499e3bd547..99f06354d74 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -73,7 +73,8 @@ private newtype TPrintAstNode = THTMLAttributesNodes(HTML::Element e) { shouldPrint(e, _) and not isNotNeeded(e) } or THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or - THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } + THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or + TRegExpTermNode(RegExpTerm term) { term.isUsedAsRegExp() } /** * A node in the output tree. @@ -282,6 +283,39 @@ private module PrintJavaScript { } } + /** + * A print node for regexp literals. + * + * The single child of this node is the root `RegExpTerm`. + */ + class RegexpNode extends ElementNode { + override RegExpLiteral element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and + result.(RegExpTermNode).getTerm() = element.getRoot() + } + } + + /** + * A print node for regexp terms. + */ + class RegExpTermNode extends PrintAstNode, TRegExpTermNode { + RegExpTerm term; + + RegExpTermNode() { this = TRegExpTermNode(term) } + + RegExpTerm getTerm() { result = term } + + override PrintAstNode getChild(int childIndex) { + result.(RegExpTermNode).getTerm() = term.getChild(childIndex) + } + + override string toString() { result = getQlClass(term) + term.toString() } + + override Location getLocation() { result = term.getLocation() } + } + /** * An aggregate node representing all the arguments for an function invocation. */ diff --git a/javascript/ql/src/semmle/javascript/Regexp.qll b/javascript/ql/src/semmle/javascript/Regexp.qll index a34b052005d..d3b7e9cac7e 100644 --- a/javascript/ql/src/semmle/javascript/Regexp.qll +++ b/javascript/ql/src/semmle/javascript/Regexp.qll @@ -215,7 +215,9 @@ class InfiniteRepetitionQuantifier extends RegExpQuantifier { * \w * ``` */ -class RegExpEscape extends RegExpTerm, @regexp_escape { } +class RegExpEscape extends RegExpTerm, @regexp_escape { + override string getAPrimaryQlClass() { result = "RegExpEscape" } +} /** * A constant regular expression term, that is, a regular expression @@ -240,6 +242,8 @@ class RegExpConstant extends RegExpTerm, @regexp_constant { override predicate isNullable() { none() } override string getConstantValue() { result = getValue() } + + override string getAPrimaryQlClass() { result = "RegExpConstant" } } /** @@ -264,6 +268,8 @@ class RegExpCharEscape extends RegExpEscape, RegExpConstant, @regexp_char_escape ) ) } + + override string getAPrimaryQlClass() { result = "RegExpCharEscape" } } /** @@ -285,6 +291,8 @@ class RegExpAlt extends RegExpTerm, @regexp_alt { override predicate isNullable() { getAlternative().isNullable() } override string getAMatchedString() { result = getAlternative().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpAlt" } } /** @@ -332,6 +340,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq { result = this.getChild(i + 1) ) } + + override string getAPrimaryQlClass() { result = "RegExpSequence" } } /** @@ -346,6 +356,8 @@ class RegExpSequence extends RegExpTerm, @regexp_seq { */ class RegExpAnchor extends RegExpTerm, @regexp_anchor { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpAnchor" } } /** @@ -357,7 +369,9 @@ class RegExpAnchor extends RegExpTerm, @regexp_anchor { * ^ * ``` */ -class RegExpCaret extends RegExpAnchor, @regexp_caret { } +class RegExpCaret extends RegExpAnchor, @regexp_caret { + override string getAPrimaryQlClass() { result = "RegExpCaret" } +} /** * A dollar assertion `$` matching the end of a line. @@ -368,7 +382,9 @@ class RegExpCaret extends RegExpAnchor, @regexp_caret { } * $ * ``` */ -class RegExpDollar extends RegExpAnchor, @regexp_dollar { } +class RegExpDollar extends RegExpAnchor, @regexp_dollar { + override string getAPrimaryQlClass() { result = "RegExpDollar" } +} /** * A word boundary assertion. @@ -381,6 +397,8 @@ class RegExpDollar extends RegExpAnchor, @regexp_dollar { } */ class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpWordBoundary" } } /** @@ -394,6 +412,8 @@ class RegExpWordBoundary extends RegExpTerm, @regexp_wordboundary { */ class RegExpNonWordBoundary extends RegExpTerm, @regexp_nonwordboundary { override predicate isNullable() { any() } + + override string getAPrimaryQlClass() { result = "RegExpNonWordBoundary" } } /** @@ -425,7 +445,9 @@ class RegExpSubPattern extends RegExpTerm, @regexp_subpattern { * (?!\n) * ``` */ -class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { } +class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { + override string getAPrimaryQlClass() { result = "RegExpLookahead" } +} /** * A zero-width lookbehind assertion. @@ -437,7 +459,9 @@ class RegExpLookahead extends RegExpSubPattern, @regexp_lookahead { } * (?` @@ -770,6 +832,8 @@ class RegExpBackRef extends RegExpTerm, @regexp_backref { } override predicate isNullable() { getGroup().isNullable() } + + override string getAPrimaryQlClass() { result = "RegExpBackRef" } } /** @@ -808,6 +872,8 @@ class RegExpCharacterClass extends RegExpTerm, @regexp_char_class { cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() ) } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClass" } } /** @@ -827,6 +893,8 @@ class RegExpCharacterRange extends RegExpTerm, @regexp_char_range { lo = getChild(0).(RegExpConstant).getValue() and hi = getChild(1).(RegExpConstant).getValue() } + + override string getAPrimaryQlClass() { result = "RegExpCharacterRange" } } /** A parse error encountered while processing a regular expression literal. */ diff --git a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts index 77f17538e18..d36be434986 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts @@ -1,2 +1,4 @@ // Dummy file to be imported so the other files are seen as modules. export let x = 5; + +export let reg = /ab+c/; \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index bd50eaa28ec..1f39b308479 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -74,6 +74,17 @@ nodes | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.label | [VarDecl] x | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | semmle.label | [VariableDeclarator] x = 5 | | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | [Literal] 5 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.label | [ExportDeclaration] export ... /ab+c/; | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.order | 13 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | [DeclStmt] let reg = ... | +| dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | [VarDecl] reg | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | [VariableDeclarator] reg = /ab+c/ | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | [RegExpLiteral] /ab+c/ | +| dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | [RegExpSequence] ab+c | +| dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | [RegExpPlus] b+ | +| dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | @@ -85,7 +96,7 @@ nodes | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.label | [DeclStmt] let foo = ... | -| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 13 | +| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 14 | | middle-rest.ts:1:5:1:7 | [VarDecl] foo | semmle.label | [VarDecl] foo | | middle-rest.ts:1:5:1:39 | [VariableDeclarator] foo: [b ... number] | semmle.label | [VariableDeclarator] foo: [b ... number] | | middle-rest.ts:1:10:1:39 | [TupleTypeExpr] [boolea ... number] | semmle.label | [TupleTypeExpr] [boolea ... number] | @@ -97,55 +108,55 @@ nodes | middle-rest.ts:3:1:3:3 | [VarRef] foo | semmle.label | [VarRef] foo | | middle-rest.ts:3:1:3:26 | [AssignExpr] foo = [ ... ", 123] | semmle.label | [AssignExpr] foo = [ ... ", 123] | | middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.label | [ExprStmt] foo = [ ... , 123]; | -| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 14 | +| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 15 | | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | semmle.label | [ArrayExpr] [true, "hello", 123] | | middle-rest.ts:3:8:3:11 | [Literal] true | semmle.label | [Literal] true | | middle-rest.ts:3:14:3:20 | [Literal] "hello" | semmle.label | [Literal] "hello" | | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.label | [Literal] 123 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 15 | +| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 16 | | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | tst.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | tst.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.label | [DeclStmt] var numVar = ... | -| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 16 | +| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 17 | | tst.ts:3:5:3:10 | [VarDecl] numVar | semmle.label | [VarDecl] numVar | | tst.ts:3:5:3:18 | [VariableDeclarator] numVar: number | semmle.label | [VariableDeclarator] numVar: number | | tst.ts:3:13:3:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.label | [DeclStmt] var num1 = ... | -| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 17 | +| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 18 | | tst.ts:5:5:5:8 | [VarDecl] num1 | semmle.label | [VarDecl] num1 | | tst.ts:5:5:5:17 | [VariableDeclarator] num1 = numVar | semmle.label | [VariableDeclarator] num1 = numVar | | tst.ts:5:12:5:17 | [VarRef] numVar | semmle.label | [VarRef] numVar | | tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.label | [DeclStmt] var num2 = ... | -| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 18 | +| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 19 | | tst.ts:6:5:6:8 | [VarDecl] num2 | semmle.label | [VarDecl] num2 | | tst.ts:6:5:6:12 | [VariableDeclarator] num2 = 5 | semmle.label | [VariableDeclarator] num2 = 5 | | tst.ts:6:12:6:12 | [Literal] 5 | semmle.label | [Literal] 5 | | tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.label | [DeclStmt] var num3 = ... | -| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 19 | +| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 20 | | tst.ts:7:5:7:8 | [VarDecl] num3 | semmle.label | [VarDecl] num3 | | tst.ts:7:5:7:22 | [VariableDeclarator] num3 = num1 + num2 | semmle.label | [VariableDeclarator] num3 = num1 + num2 | | tst.ts:7:12:7:15 | [VarRef] num1 | semmle.label | [VarRef] num1 | | tst.ts:7:12:7:22 | [BinaryExpr] num1 + num2 | semmle.label | [BinaryExpr] num1 + num2 | | tst.ts:7:19:7:22 | [VarRef] num2 | semmle.label | [VarRef] num2 | | tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.label | [DeclStmt] var strVar = ... | -| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 20 | +| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 21 | | tst.ts:9:5:9:10 | [VarDecl] strVar | semmle.label | [VarDecl] strVar | | tst.ts:9:5:9:18 | [VariableDeclarator] strVar: string | semmle.label | [VariableDeclarator] strVar: string | | tst.ts:9:13:9:18 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.label | [DeclStmt] var hello = ... | -| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 21 | +| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 22 | | tst.ts:10:5:10:9 | [VarDecl] hello | semmle.label | [VarDecl] hello | | tst.ts:10:5:10:19 | [VariableDeclarator] hello = "hello" | semmle.label | [VariableDeclarator] hello = "hello" | | tst.ts:10:13:10:19 | [Literal] "hello" | semmle.label | [Literal] "hello" | | tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.label | [DeclStmt] var world = ... | -| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 22 | +| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 23 | | tst.ts:11:5:11:9 | [VarDecl] world | semmle.label | [VarDecl] world | | tst.ts:11:5:11:19 | [VariableDeclarator] world = "world" | semmle.label | [VariableDeclarator] world = "world" | | tst.ts:11:13:11:19 | [Literal] "world" | semmle.label | [Literal] "world" | | tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.label | [DeclStmt] var msg = ... | -| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 23 | +| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 24 | | tst.ts:12:5:12:7 | [VarDecl] msg | semmle.label | [VarDecl] msg | | tst.ts:12:5:12:29 | [VariableDeclarator] msg = h ... + world | semmle.label | [VariableDeclarator] msg = h ... + world | | tst.ts:12:11:12:15 | [VarRef] hello | semmle.label | [VarRef] hello | @@ -154,7 +165,7 @@ nodes | tst.ts:12:19:12:21 | [Literal] " " | semmle.label | [Literal] " " | | tst.ts:12:25:12:29 | [VarRef] world | semmle.label | [VarRef] world | | tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 24 | +| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | | tst.ts:14:10:14:15 | [VarDecl] concat | semmle.label | [VarDecl] concat | | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:14:20:14:25 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -167,7 +178,7 @@ nodes | tst.ts:14:56:14:60 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:14:60:14:60 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | +| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | | tst.ts:16:10:16:12 | [VarDecl] add | semmle.label | [VarDecl] add | | tst.ts:16:14:16:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:16:17:16:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | @@ -180,7 +191,7 @@ nodes | tst.ts:16:53:16:57 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:16:57:16:57 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | +| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | | tst.ts:18:10:18:16 | [VarDecl] untyped | semmle.label | [VarDecl] untyped | | tst.ts:18:18:18:18 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:18:21:18:21 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -190,7 +201,7 @@ nodes | tst.ts:18:33:18:37 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:18:37:18:37 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | +| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 28 | | tst.ts:20:10:20:21 | [VarDecl] partialTyped | semmle.label | [VarDecl] partialTyped | | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -201,7 +212,7 @@ nodes | tst.ts:20:46:20:50 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:20:50:20:50 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.label | [ForOfStmt] for (le ... 2]) {} | -| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 28 | +| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 29 | | tst.ts:22:6:22:20 | [DeclStmt] let numFromLoop = ... | semmle.label | [DeclStmt] let numFromLoop = ... | | tst.ts:22:10:22:20 | [VarDecl] numFromLoop | semmle.label | [VarDecl] numFromLoop | | tst.ts:22:10:22:20 | [VariableDeclarator] numFromLoop | semmle.label | [VariableDeclarator] numFromLoop | @@ -210,54 +221,54 @@ nodes | tst.ts:22:29:22:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:22:33:22:34 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.label | [DeclStmt] let array = ... | -| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 29 | +| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 30 | | tst.ts:24:5:24:9 | [VarDecl] array | semmle.label | [VarDecl] array | | tst.ts:24:5:24:19 | [VariableDeclarator] array: number[] | semmle.label | [VariableDeclarator] array: number[] | | tst.ts:24:12:24:17 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:24:12:24:19 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.label | [DeclStmt] let voidType = ... | -| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 30 | +| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 31 | | tst.ts:26:5:26:12 | [VarDecl] voidType | semmle.label | [VarDecl] voidType | | tst.ts:26:5:26:24 | [VariableDeclarator] voidType: () => void | semmle.label | [VariableDeclarator] voidType: () => void | | tst.ts:26:15:26:24 | [FunctionExpr] () => void | semmle.label | [FunctionExpr] () => void | | tst.ts:26:15:26:24 | [FunctionTypeExpr] () => void | semmle.label | [FunctionTypeExpr] () => void | | tst.ts:26:21:26:24 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.label | [DeclStmt] let undefinedType = ... | -| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 31 | +| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 32 | | tst.ts:27:5:27:17 | [VarDecl] undefinedType | semmle.label | [VarDecl] undefinedType | | tst.ts:27:5:27:28 | [VariableDeclarator] undefin ... defined | semmle.label | [VariableDeclarator] undefin ... defined | | tst.ts:27:20:27:28 | [KeywordTypeExpr] undefined | semmle.label | [KeywordTypeExpr] undefined | | tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.label | [DeclStmt] let nullType = ... | -| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 32 | +| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 33 | | tst.ts:28:5:28:12 | [VarDecl] nullType | semmle.label | [VarDecl] nullType | | tst.ts:28:5:28:25 | [VariableDeclarator] nullTyp ... = null | semmle.label | [VariableDeclarator] nullTyp ... = null | | tst.ts:28:15:28:18 | [KeywordTypeExpr] null | semmle.label | [KeywordTypeExpr] null | | tst.ts:28:22:28:25 | [Literal] null | semmle.label | [Literal] null | | tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.label | [DeclStmt] let neverType = ... | -| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 33 | +| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 34 | | tst.ts:29:5:29:13 | [VarDecl] neverType | semmle.label | [VarDecl] neverType | | tst.ts:29:5:29:26 | [VariableDeclarator] neverTy ... > never | semmle.label | [VariableDeclarator] neverTy ... > never | | tst.ts:29:16:29:26 | [FunctionExpr] () => never | semmle.label | [FunctionExpr] () => never | | tst.ts:29:16:29:26 | [FunctionTypeExpr] () => never | semmle.label | [FunctionTypeExpr] () => never | | tst.ts:29:22:29:26 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | | tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.label | [DeclStmt] let symbolType = ... | -| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 34 | +| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 35 | | tst.ts:30:5:30:14 | [VarDecl] symbolType | semmle.label | [VarDecl] symbolType | | tst.ts:30:5:30:22 | [VariableDeclarator] symbolType: symbol | semmle.label | [VariableDeclarator] symbolType: symbol | | tst.ts:30:17:30:22 | [KeywordTypeExpr] symbol | semmle.label | [KeywordTypeExpr] symbol | | tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.label | [DeclStmt] const uniqueSymbolType = ... | -| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 35 | +| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 36 | | tst.ts:31:7:31:22 | [VarDecl] uniqueSymbolType | semmle.label | [VarDecl] uniqueSymbolType | | tst.ts:31:7:31:44 | [VariableDeclarator] uniqueS ... = null | semmle.label | [VariableDeclarator] uniqueS ... = null | | tst.ts:31:25:31:37 | [KeywordTypeExpr] unique symbol | semmle.label | [KeywordTypeExpr] unique symbol | | tst.ts:31:41:31:44 | [Literal] null | semmle.label | [Literal] null | | tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.label | [DeclStmt] let objectType = ... | -| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 36 | +| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 37 | | tst.ts:32:5:32:14 | [VarDecl] objectType | semmle.label | [VarDecl] objectType | | tst.ts:32:5:32:22 | [VariableDeclarator] objectType: object | semmle.label | [VariableDeclarator] objectType: object | | tst.ts:32:17:32:22 | [KeywordTypeExpr] object | semmle.label | [KeywordTypeExpr] object | | tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.label | [DeclStmt] let intersection = ... | -| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 37 | +| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 38 | | tst.ts:33:5:33:16 | [VarDecl] intersection | semmle.label | [VarDecl] intersection | | tst.ts:33:5:33:38 | [VariableDeclarator] interse ... string} | semmle.label | [VariableDeclarator] interse ... string} | | tst.ts:33:19:33:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -267,14 +278,14 @@ nodes | tst.ts:33:29:33:37 | [FieldDeclaration] x: string | semmle.label | [FieldDeclaration] x: string | | tst.ts:33:32:33:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.label | [DeclStmt] let tuple = ... | -| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 38 | +| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 39 | | tst.ts:34:5:34:9 | [VarDecl] tuple | semmle.label | [VarDecl] tuple | | tst.ts:34:5:34:27 | [VariableDeclarator] tuple: ... string] | semmle.label | [VariableDeclarator] tuple: ... string] | | tst.ts:34:12:34:27 | [TupleTypeExpr] [number, string] | semmle.label | [TupleTypeExpr] [number, string] | | tst.ts:34:13:34:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:34:21:34:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.label | [DeclStmt] let tupleWithOptionalElement = ... | -| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 39 | +| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 40 | | tst.ts:36:5:36:28 | [VarDecl] tupleWithOptionalElement | semmle.label | [VarDecl] tupleWithOptionalElement | | tst.ts:36:5:36:55 | [VariableDeclarator] tupleWi ... umber?] | semmle.label | [VariableDeclarator] tupleWi ... umber?] | | tst.ts:36:31:36:55 | [TupleTypeExpr] [number ... umber?] | semmle.label | [TupleTypeExpr] [number ... umber?] | @@ -283,12 +294,12 @@ nodes | tst.ts:36:48:36:53 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:36:48:36:54 | [OptionalTypeExpr] number? | semmle.label | [OptionalTypeExpr] number? | | tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.label | [DeclStmt] let emptyTuple = ... | -| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 40 | +| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 41 | | tst.ts:37:5:37:14 | [VarDecl] emptyTuple | semmle.label | [VarDecl] emptyTuple | | tst.ts:37:5:37:18 | [VariableDeclarator] emptyTuple: [] | semmle.label | [VariableDeclarator] emptyTuple: [] | | tst.ts:37:17:37:18 | [TupleTypeExpr] [] | semmle.label | [TupleTypeExpr] [] | | tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.label | [DeclStmt] let tupleWithRestElement = ... | -| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 41 | +| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 42 | | tst.ts:38:5:38:24 | [VarDecl] tupleWithRestElement | semmle.label | [VarDecl] tupleWithRestElement | | tst.ts:38:5:38:47 | [VariableDeclarator] tupleWi ... ring[]] | semmle.label | [VariableDeclarator] tupleWi ... ring[]] | | tst.ts:38:27:38:47 | [TupleTypeExpr] [number ... ring[]] | semmle.label | [TupleTypeExpr] [number ... ring[]] | @@ -297,7 +308,7 @@ nodes | tst.ts:38:39:38:44 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:38:39:38:46 | [ArrayTypeExpr] string[] | semmle.label | [ArrayTypeExpr] string[] | | tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.label | [DeclStmt] let tupleWithOptionalAndRestElements = ... | -| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 42 | +| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 43 | | tst.ts:39:5:39:36 | [VarDecl] tupleWithOptionalAndRestElements | semmle.label | [VarDecl] tupleWithOptionalAndRestElements | | tst.ts:39:5:39:68 | [VariableDeclarator] tupleWi ... mber[]] | semmle.label | [VariableDeclarator] tupleWi ... mber[]] | | tst.ts:39:39:39:68 | [TupleTypeExpr] [number ... mber[]] | semmle.label | [TupleTypeExpr] [number ... mber[]] | @@ -308,12 +319,12 @@ nodes | tst.ts:39:60:39:65 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:39:60:39:67 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.label | [DeclStmt] let unknownType = ... | -| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 43 | +| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 44 | | tst.ts:40:5:40:15 | [VarDecl] unknownType | semmle.label | [VarDecl] unknownType | | tst.ts:40:5:40:24 | [VariableDeclarator] unknownType: unknown | semmle.label | [VariableDeclarator] unknownType: unknown | | tst.ts:40:18:40:24 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | | tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.label | [DeclStmt] let constArrayLiteral = ... | -| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 44 | +| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 45 | | tst.ts:42:5:42:21 | [VarDecl] constArrayLiteral | semmle.label | [VarDecl] constArrayLiteral | | tst.ts:42:5:42:39 | [VariableDeclarator] constAr ... s const | semmle.label | [VariableDeclarator] constAr ... s const | | tst.ts:42:25:42:30 | [ArrayExpr] [1, 2] | semmle.label | [ArrayExpr] [1, 2] | @@ -322,7 +333,7 @@ nodes | tst.ts:42:29:42:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:42:35:42:39 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.label | [DeclStmt] let constObjectLiteral = ... | -| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 45 | +| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 46 | | tst.ts:43:5:43:22 | [VarDecl] constObjectLiteral | semmle.label | [VarDecl] constObjectLiteral | | tst.ts:43:5:43:48 | [VariableDeclarator] constOb ... s const | semmle.label | [VariableDeclarator] constOb ... s const | | tst.ts:43:26:43:39 | [ObjectExpr] {foo: ...} | semmle.label | [ObjectExpr] {foo: ...} | @@ -332,7 +343,7 @@ nodes | tst.ts:43:33:43:37 | [Literal] "foo" | semmle.label | [Literal] "foo" | | tst.ts:43:44:43:48 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.label | [TryStmt] try { } ... ; } } | -| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 46 | +| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 47 | | tst.ts:46:5:46:7 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | | tst.ts:47:1:51:1 | [CatchClause] catch ( ... ; } } | semmle.label | [CatchClause] catch ( ... ; } } | | tst.ts:47:8:47:8 | [SimpleParameter] e | semmle.label | [SimpleParameter] e | @@ -349,21 +360,21 @@ nodes | tst.ts:49:15:49:20 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:49:24:49:24 | [VarRef] e | semmle.label | [VarRef] e | | tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 47 | +| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | | tst.ts:54:11:54:26 | [Identifier] NonAbstractDummy | semmle.label | [Identifier] NonAbstractDummy | | tst.ts:55:3:55:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:55:3:55:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:55:3:55:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:55:14:55:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | +| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 49 | | tst.ts:58:11:58:17 | [Identifier] HasArea | semmle.label | [Identifier] HasArea | | tst.ts:59:3:59:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:59:3:59:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:59:3:59:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:59:14:59:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.label | [DeclStmt] let Ctor = ... | -| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 49 | +| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 50 | | tst.ts:63:5:63:8 | [VarDecl] Ctor | semmle.label | [VarDecl] Ctor | | tst.ts:63:5:63:44 | [VariableDeclarator] Ctor: a ... = Shape | semmle.label | [VariableDeclarator] Ctor: a ... = Shape | | tst.ts:63:11:63:36 | [FunctionExpr] abstrac ... HasArea | semmle.label | [FunctionExpr] abstrac ... HasArea | @@ -371,7 +382,7 @@ nodes | tst.ts:63:30:63:36 | [LocalTypeAccess] HasArea | semmle.label | [LocalTypeAccess] HasArea | | tst.ts:63:40:63:44 | [VarRef] Shape | semmle.label | [VarRef] Shape | | tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 50 | +| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 51 | | tst.ts:65:6:65:12 | [Identifier] MyUnion | semmle.label | [Identifier] MyUnion | | tst.ts:65:16:65:30 | [InterfaceTypeExpr] {myUnion: true} | semmle.label | [InterfaceTypeExpr] {myUnion: true} | | tst.ts:65:16:65:53 | [UnionTypeExpr] {myUnio ... : true} | semmle.label | [UnionTypeExpr] {myUnio ... : true} | @@ -383,7 +394,7 @@ nodes | tst.ts:65:35:65:52 | [FieldDeclaration] stillMyUnion: true | semmle.label | [FieldDeclaration] stillMyUnion: true | | tst.ts:65:49:65:52 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.label | [DeclStmt] let union1 = ... | -| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 51 | +| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 52 | | tst.ts:66:5:66:10 | [VarDecl] union1 | semmle.label | [VarDecl] union1 | | tst.ts:66:5:66:37 | [VariableDeclarator] union1: ... : true} | semmle.label | [VariableDeclarator] union1: ... : true} | | tst.ts:66:13:66:19 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | @@ -392,7 +403,7 @@ nodes | tst.ts:66:24:66:36 | [Property] myUnion: true | semmle.label | [Property] myUnion: true | | tst.ts:66:33:66:36 | [Literal] true | semmle.label | [Literal] true | | tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 52 | +| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 53 | | tst.ts:68:6:68:13 | [Identifier] MyUnion2 | semmle.label | [Identifier] MyUnion2 | | tst.ts:68:17:68:23 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | | tst.ts:68:17:68:48 | [UnionTypeExpr] MyUnion ... : true} | semmle.label | [UnionTypeExpr] MyUnion ... : true} | @@ -401,7 +412,7 @@ nodes | tst.ts:68:28:68:47 | [FieldDeclaration] yetAnotherType: true | semmle.label | [FieldDeclaration] yetAnotherType: true | | tst.ts:68:44:68:47 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.label | [DeclStmt] let union2 = ... | -| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 53 | +| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 54 | | tst.ts:69:5:69:10 | [VarDecl] union2 | semmle.label | [VarDecl] union2 | | tst.ts:69:5:69:45 | [VariableDeclarator] union2: ... : true} | semmle.label | [VariableDeclarator] union2: ... : true} | | tst.ts:69:13:69:20 | [LocalTypeAccess] MyUnion2 | semmle.label | [LocalTypeAccess] MyUnion2 | @@ -410,16 +421,16 @@ nodes | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | semmle.label | [Property] yetAnotherType: true | | tst.ts:69:41:69:44 | [Literal] true | semmle.label | [Literal] true | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | -| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 54 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 55 | | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B | | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | | type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... | -| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 55 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 56 | | type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b | | type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B | | type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B | | type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | -| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 56 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 57 | | type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray | | type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T | | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -431,14 +442,14 @@ nodes | type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... | -| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 57 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 58 | | type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> | | type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray | | type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | -| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 58 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 59 | | type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json | | type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] | | type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -454,12 +465,12 @@ nodes | type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] | | type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... | -| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 59 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 60 | | type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json | | type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json | | type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | -| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 60 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 61 | | type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode | | type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] | | type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -475,7 +486,7 @@ nodes | type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | | type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] | | type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... | -| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 61 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 62 | | type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode | | type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] | | type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | @@ -500,12 +511,12 @@ nodes | type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" | | type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" | | type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 62 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 63 | | type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} | -| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 63 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 64 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} | | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | @@ -513,36 +524,36 @@ nodes | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... | -| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 64 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 65 | | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj | | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C | | type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C | | type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} | -| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 65 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 66 | | type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} | | type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E | | type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... | -| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 66 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 67 | | type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj | | type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E | | type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E | | type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} | -| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 67 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 68 | | type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} | | type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N | | type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | | type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... | -| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 68 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 69 | | type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj | | type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N | | type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N | | type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 69 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 70 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | -| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 70 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 71 | | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I | | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -550,14 +561,14 @@ nodes | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; | | type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | | type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... | -| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 71 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 72 | | type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i | | type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I | | type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I | | type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I | | type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } | -| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 72 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 73 | | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | @@ -569,14 +580,14 @@ nodes | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T | | type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... | -| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 73 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 74 | | type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C | | type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C | | type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C | | type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } | -| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 74 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 75 | | type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color | | type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red | | type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red | @@ -585,29 +596,29 @@ nodes | type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue | | type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue | | type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... | -| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 75 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 76 | | type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color | | type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color | | type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color | | type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } | -| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 76 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 77 | | type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember | | type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member | | type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member | | type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... | -| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 77 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 78 | | type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e | | type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember | | type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember | | type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | -| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 78 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 79 | | type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias | | type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T | | type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | | type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] | | type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... | -| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 79 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 80 | | type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray | | type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> | | type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias | @@ -716,6 +727,24 @@ edges | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.order | 1 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | 2 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.order | 2 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | 1 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.order | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | 2 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.order | 2 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | 0 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.order | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | 2 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.order | 2 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | 0 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index c9cad8a1443..7c8159a9b10 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -15,6 +15,8 @@ getExprType | boolean-type.ts:15:5:15:12 | boolean6 | boolean | | dummy.ts:2:12:2:12 | x | number | | dummy.ts:2:16:2:16 | 5 | 5 | +| dummy.ts:4:12:4:14 | reg | RegExp | +| dummy.ts:4:18:4:23 | /ab+c/ | RegExp | | middle-rest.ts:1:5:1:7 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:3 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:26 | foo = [ ... ", 123] | [true, string, number] | From 504c34ed2c1493b0f7b413e710e8545d8ebfd21f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 10 May 2021 14:51:13 +0200 Subject: [PATCH 291/550] use shouldPrint to filter out regular expressions from other files --- javascript/ql/src/semmle/javascript/PrintAst.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 99f06354d74..029cfd536fc 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -74,7 +74,11 @@ private newtype TPrintAstNode = THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or - TRegExpTermNode(RegExpTerm term) { term.isUsedAsRegExp() } + TRegExpTermNode(RegExpTerm term) { + shouldPrint(term, _) and + term.isUsedAsRegExp() and + any(RegExpLiteral lit).getRoot() = term.getRootTerm() + } /** * A node in the output tree. From 78370cf63fa0bf528c3f566654c73bdf25547f1f Mon Sep 17 00:00:00 2001 From: yoff Date: Mon, 10 May 2021 14:53:40 +0200 Subject: [PATCH 292/550] Update python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 05ea4f62630..4f3457e0a99 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -58,7 +58,7 @@ private module Re { * This class will identify that `re.compile` compiles `input` and afterwards * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) - * + * * * See `RegexExecutionMethods` * From c7cd75437ffb4748c63239eb95f72efa537c0f2b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 14:58:33 +0200 Subject: [PATCH 293/550] C++: Add testcase demonstrating false positive from conversions. --- .../ComparisonWithWiderType.expected | 1 + .../CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected index d04bff0a812..652a34f98f1 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected @@ -1,3 +1,4 @@ +| test3.cpp:6:8:6:71 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type int. | test3.cpp:5:34:5:38 | small | small | test3.cpp:6:42:6:70 | ... - ... | ... - ... | | test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x | | test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x | | test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp index 3ae02d9d56c..cbd9c2e5bd9 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp @@ -1,3 +1,7 @@ void test_issue_5850(unsigned char small, unsigned int large1) { for(; small < static_cast(large1 - 1); small++) { } // GOOD -} \ No newline at end of file +} + +void test_widening(unsigned char small, char large) { + for(; small < static_cast(static_cast(large) - 1); small++) { } // GOOD [FALSE POSITIVE] +} From 3fe9a3d9334699a4db47ebd6ee9e5c3d2640cd45 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 12:15:40 +0200 Subject: [PATCH 294/550] Python: Add modeling of simplejson PyPI package I noticed that we don't handle PostUpdateNote very well in the concept tests, for exmaple for `json.dump(...)` there _should_ have been an `encodeOutput` as part of the inline expectations. I'll work on fixing that up in a separate PR, to keep things clean. --- docs/codeql/support/reusables/frameworks.rst | 1 + .../2021-05-10-simplejson-add-modeling.md | 2 + python/ql/src/semmle/python/Frameworks.qll | 1 + .../semmle/python/frameworks/Simplejson.qll | 84 +++++++++++++++++++ .../simplejson/ConceptsTest.expected | 0 .../frameworks/simplejson/ConceptsTest.ql | 2 + .../simplejson/InlineTaintTest.expected | 3 + .../frameworks/simplejson/InlineTaintTest.ql | 1 + .../frameworks/simplejson/taint_test.py | 46 ++++++++++ 9 files changed, 140 insertions(+) create mode 100644 python/change-notes/2021-05-10-simplejson-add-modeling.md create mode 100644 python/ql/src/semmle/python/frameworks/Simplejson.qll create mode 100644 python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected create mode 100644 python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.ql create mode 100644 python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected create mode 100644 python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql create mode 100644 python/ql/test/library-tests/frameworks/simplejson/taint_test.py diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index d54c07454bc..4ea461ed435 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -156,6 +156,7 @@ Python built-in support Tornado, Web framework PyYAML, Serialization dill, Serialization + simplejson, Serialization fabric, Utility library invoke, Utility library idna, Utility library diff --git a/python/change-notes/2021-05-10-simplejson-add-modeling.md b/python/change-notes/2021-05-10-simplejson-add-modeling.md new file mode 100644 index 00000000000..910441bdeac --- /dev/null +++ b/python/change-notes/2021-05-10-simplejson-add-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added modeling of the PyPI package `simplejson`. diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll index ed8f308c70a..bec73652394 100644 --- a/python/ql/src/semmle/python/Frameworks.qll +++ b/python/ql/src/semmle/python/Frameworks.qll @@ -16,6 +16,7 @@ private import semmle.python.frameworks.MysqlConnectorPython private import semmle.python.frameworks.MySQLdb private import semmle.python.frameworks.Psycopg2 private import semmle.python.frameworks.PyMySQL +private import semmle.python.frameworks.Simplejson private import semmle.python.frameworks.Stdlib private import semmle.python.frameworks.Tornado private import semmle.python.frameworks.Yaml diff --git a/python/ql/src/semmle/python/frameworks/Simplejson.qll b/python/ql/src/semmle/python/frameworks/Simplejson.qll new file mode 100644 index 00000000000..41676705a40 --- /dev/null +++ b/python/ql/src/semmle/python/frameworks/Simplejson.qll @@ -0,0 +1,84 @@ +/** + * Provides classes modeling security-relevant aspects of the `simplejson` PyPI package. + * See https://simplejson.readthedocs.io/en/latest/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for the `simplejson` PyPI package. + * See https://simplejson.readthedocs.io/en/latest/. + */ +private module SimplejsonModel { + /** + * A call to `simplejson.dumps`. + * + * See https://simplejson.readthedocs.io/en/latest/#simplejson.dumps + */ + private class SimplejsonDumpsCall extends Encoding::Range, DataFlow::CallCfgNode { + SimplejsonDumpsCall() { this = API::moduleImport("simplejson").getMember("dumps").getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + } + + /** + * A call to `simplejson.dump`. + * + * See https://simplejson.readthedocs.io/en/latest/#simplejson.dump + */ + private class SimplejsonDumpCall extends Encoding::Range, DataFlow::CallCfgNode { + SimplejsonDumpCall() { this = API::moduleImport("simplejson").getMember("dump").getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } + + override DataFlow::Node getOutput() { + result.(DataFlow::PostUpdateNode).getPreUpdateNode() in [ + this.getArg(1), this.getArgByName("fp") + ] + } + + override string getFormat() { result = "JSON" } + } + + /** + * A call to `simplejson.loads`. + * + * See https://simplejson.readthedocs.io/en/latest/#simplejson.loads + */ + private class SimplejsonLoadsCall extends Decoding::Range, DataFlow::CallCfgNode { + SimplejsonLoadsCall() { this = API::moduleImport("simplejson").getMember("loads").getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("s")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + + override predicate mayExecuteInput() { none() } + } + + /** + * A call to `simplejson.load`. + * + * See https://simplejson.readthedocs.io/en/latest/#simplejson.load + */ + private class SimplejsonLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + SimplejsonLoadCall() { this = API::moduleImport("simplejson").getMember("load").getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("fp")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + + override predicate mayExecuteInput() { none() } + } +} diff --git a/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected new file mode 100644 index 00000000000..79d760d87f4 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected @@ -0,0 +1,3 @@ +argumentToEnsureNotTaintedNotMarkedAsSpurious +untaintedArgumentToEnsureTaintedNotMarkedAsMissing +failures diff --git a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql new file mode 100644 index 00000000000..027ad8667be --- /dev/null +++ b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql @@ -0,0 +1 @@ +import experimental.meta.InlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py new file mode 100644 index 00000000000..2b7de24a0c2 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py @@ -0,0 +1,46 @@ +import simplejson +from io import StringIO + +def test(): + ts = TAINTED_STRING + tainted_obj = {"foo": ts} + + encoded = simplejson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + + ensure_tainted( + encoded, # $ tainted + simplejson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + simplejson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + simplejson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + simplejson.loads(s=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + ) + + # load/dump with file-like + tainted_filelike = StringIO() + simplejson.dump(tainted_obj, tainted_filelike) # $ encodeFormat=JSON encodeInput=tainted_obj + + tainted_filelike.seek(0) + ensure_tainted( + tainted_filelike, # $ tainted + simplejson.load(tainted_filelike), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike + ) + + # load/dump with file-like using keyword-args + tainted_filelike = StringIO() + simplejson.dump(obj=tainted_obj, fp=tainted_filelike) # $ encodeFormat=JSON encodeInput=tainted_obj + + tainted_filelike.seek(0) + ensure_tainted( + tainted_filelike, # $ tainted + simplejson.load(fp=tainted_filelike), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike + ) + +# To make things runable + +TAINTED_STRING = "TAINTED_STRING" +def ensure_tainted(*args): + print("- ensure_tainted") + for i, arg in enumerate(args): + print("arg {}: {!r}".format(i, arg)) + +test() From 784e0cdb96b1f5dbe72aecc0d595e78cdd6dd085 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 14:28:54 +0200 Subject: [PATCH 295/550] Python: Improve tests of `json` module Inspired by the work on previous commit --- .../defaultAdditionalTaintStep/test_json.py | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py index a7398b4d001..46ac4afc219 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py @@ -11,54 +11,43 @@ if TYPE_CHECKING: # Actual tests from io import StringIO - -# Workaround for Python3 not having unicode -import sys -if sys.version_info[0] == 3: - unicode = str +import json def test(): print("\n# test") ts = TAINTED_STRING - import json + + encoded = json.dumps(ts) ensure_tainted( + encoded, # $ tainted json.dumps(ts), # $ tainted - json.loads(json.dumps(ts)), # $ tainted + json.dumps(obj=ts), # $ MISSING: tainted + json.loads(encoded), # $ tainted + json.loads(s=encoded), # $ MISSING: tainted ) - # For Python2, need to convert to unicode for StringIO to work - tainted_filelike = StringIO(unicode(json.dumps(ts))) + # load/dump with file-like + tainted_filelike = StringIO() + json.dump(ts, tainted_filelike) + tainted_filelike.seek(0) ensure_tainted( tainted_filelike, # $ MISSING: tainted json.load(tainted_filelike), # $ MISSING: tainted ) -def non_syntacical(): - print("\n# non_syntacical") - ts = TAINTED_STRING - - # a less syntactical approach - from json import load, loads, dumps - - dumps_alias = dumps - - ensure_tainted( - dumps(ts), # $ tainted - dumps_alias(ts), # $ tainted - loads(dumps(ts)), # $ tainted - ) - - # For Python2, need to convert to unicode for StringIO to work - tainted_filelike = StringIO(unicode(dumps(ts))) + # load/dump with file-like using keyword-args + tainted_filelike = StringIO() + json.dump(obj=ts, fp=tainted_filelike) + tainted_filelike.seek(0) ensure_tainted( tainted_filelike, # $ MISSING: tainted - load(tainted_filelike), # $ MISSING: tainted + json.load(fp=tainted_filelike), # $ MISSING: tainted ) + # Make tests runable test() -non_syntacical() From 63f28d7d9b1946b97c1b3150957268ac5e316c9e Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 14:33:27 +0200 Subject: [PATCH 296/550] Python: Model keyword args to json loads/dumps --- python/ql/src/semmle/python/frameworks/Stdlib.qll | 4 ++-- .../tainttracking/defaultAdditionalTaintStep/test_json.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index 07753e9fde5..afc1fb45f25 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -511,7 +511,7 @@ private module Stdlib { override predicate mayExecuteInput() { none() } - override DataFlow::Node getAnInput() { result.asCfgNode() = node.getArg(0) } + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("s")] } override DataFlow::Node getOutput() { result = this } @@ -525,7 +525,7 @@ private module Stdlib { private class JsonDumpsCall extends Encoding::Range, DataFlow::CallCfgNode { JsonDumpsCall() { this = json().getMember("dumps").getACall() } - override DataFlow::Node getAnInput() { result.asCfgNode() = node.getArg(0) } + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } override DataFlow::Node getOutput() { result = this } diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py index 46ac4afc219..f91bcacd3bc 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py @@ -22,9 +22,9 @@ def test(): ensure_tainted( encoded, # $ tainted json.dumps(ts), # $ tainted - json.dumps(obj=ts), # $ MISSING: tainted + json.dumps(obj=ts), # $ tainted json.loads(encoded), # $ tainted - json.loads(s=encoded), # $ MISSING: tainted + json.loads(s=encoded), # $ tainted ) # load/dump with file-like From 72d08f4d6eeea58d96bd8a252203caf687ee93ed Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 14:35:33 +0200 Subject: [PATCH 297/550] Python: Model json load/dump --- .../src/semmle/python/frameworks/Stdlib.qll | 34 +++++++++++++++++++ .../defaultAdditionalTaintStep/test_json.py | 8 ++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/python/ql/src/semmle/python/frameworks/Stdlib.qll b/python/ql/src/semmle/python/frameworks/Stdlib.qll index afc1fb45f25..69023b8940a 100644 --- a/python/ql/src/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/semmle/python/frameworks/Stdlib.qll @@ -518,6 +518,22 @@ private module Stdlib { override string getFormat() { result = "JSON" } } + /** + * A call to `json.load` + * See https://docs.python.org/3/library/json.html#json.load + */ + private class JsonLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + JsonLoadCall() { this = json().getMember("load").getACall() } + + override predicate mayExecuteInput() { none() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("fp")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + } + /** * A call to `json.dumps` * See https://docs.python.org/3/library/json.html#json.dumps @@ -532,6 +548,24 @@ private module Stdlib { override string getFormat() { result = "JSON" } } + /** + * A call to `json.dump` + * See https://docs.python.org/3/library/json.html#json.dump + */ + private class JsonDumpCall extends Encoding::Range, DataFlow::CallCfgNode { + JsonDumpCall() { this = json().getMember("dump").getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } + + override DataFlow::Node getOutput() { + result.(DataFlow::PostUpdateNode).getPreUpdateNode() in [ + this.getArg(1), this.getArgByName("fp") + ] + } + + override string getFormat() { result = "JSON" } + } + // --------------------------------------------------------------------------- // cgi // --------------------------------------------------------------------------- diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py index f91bcacd3bc..b1b0536b03b 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/test_json.py @@ -33,8 +33,8 @@ def test(): tainted_filelike.seek(0) ensure_tainted( - tainted_filelike, # $ MISSING: tainted - json.load(tainted_filelike), # $ MISSING: tainted + tainted_filelike, # $ tainted + json.load(tainted_filelike), # $ tainted ) # load/dump with file-like using keyword-args @@ -43,8 +43,8 @@ def test(): tainted_filelike.seek(0) ensure_tainted( - tainted_filelike, # $ MISSING: tainted - json.load(fp=tainted_filelike), # $ MISSING: tainted + tainted_filelike, # $ tainted + json.load(fp=tainted_filelike), # $ tainted ) From c2a6b811fcb2236c2400a18a54041543aa729dbc Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 15:07:55 +0200 Subject: [PATCH 298/550] Python: Add modeling of ujson PyPI package The problem with `tainted_filelike` not having taint, is that in the call `ujson.dump(tainted_obj, tainted_filelike)` there is no PostUpdateNote for `tainted_filelike` :( The reason is that points-to is not able to resolve the call, so none of the clauses in `argumentPreUpdateNode` matches See https://github.com/github/codeql/blob/08731fc6cf4ba6951cd4e8f239eac1f3388d3957/python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll#L101-L111 Let's deal with that issue in an other PR though --- docs/codeql/support/reusables/frameworks.rst | 1 + .../2021-05-10-ujson-add-modeling.md | 2 + python/ql/src/semmle/python/Frameworks.qll | 1 + .../ql/src/semmle/python/frameworks/Ujson.qll | 76 +++++++++++++++++++ .../frameworks/ujson/ConceptsTest.expected | 0 .../frameworks/ujson/ConceptsTest.ql | 2 + .../frameworks/ujson/InlineTaintTest.expected | 3 + .../frameworks/ujson/InlineTaintTest.ql | 1 + .../frameworks/ujson/taint_test.py | 44 +++++++++++ 9 files changed, 130 insertions(+) create mode 100644 python/change-notes/2021-05-10-ujson-add-modeling.md create mode 100644 python/ql/src/semmle/python/frameworks/Ujson.qll create mode 100644 python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected create mode 100644 python/ql/test/library-tests/frameworks/ujson/ConceptsTest.ql create mode 100644 python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected create mode 100644 python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql create mode 100644 python/ql/test/library-tests/frameworks/ujson/taint_test.py diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 4ea461ed435..5bc7d572e7f 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -157,6 +157,7 @@ Python built-in support PyYAML, Serialization dill, Serialization simplejson, Serialization + ujson, Serialization fabric, Utility library invoke, Utility library idna, Utility library diff --git a/python/change-notes/2021-05-10-ujson-add-modeling.md b/python/change-notes/2021-05-10-ujson-add-modeling.md new file mode 100644 index 00000000000..2d7cdc4b68a --- /dev/null +++ b/python/change-notes/2021-05-10-ujson-add-modeling.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added modeling of the PyPI package `ujson`. diff --git a/python/ql/src/semmle/python/Frameworks.qll b/python/ql/src/semmle/python/Frameworks.qll index bec73652394..3115c3ffac6 100644 --- a/python/ql/src/semmle/python/Frameworks.qll +++ b/python/ql/src/semmle/python/Frameworks.qll @@ -19,4 +19,5 @@ private import semmle.python.frameworks.PyMySQL private import semmle.python.frameworks.Simplejson private import semmle.python.frameworks.Stdlib private import semmle.python.frameworks.Tornado +private import semmle.python.frameworks.Ujson private import semmle.python.frameworks.Yaml diff --git a/python/ql/src/semmle/python/frameworks/Ujson.qll b/python/ql/src/semmle/python/frameworks/Ujson.qll new file mode 100644 index 00000000000..145f7f883a9 --- /dev/null +++ b/python/ql/src/semmle/python/frameworks/Ujson.qll @@ -0,0 +1,76 @@ +/** + * Provides classes modeling security-relevant aspects of the `ujson` PyPI package. + * See https://pypi.org/project/ujson/. + */ + +private import python +private import semmle.python.dataflow.new.DataFlow +private import semmle.python.dataflow.new.TaintTracking +private import semmle.python.Concepts +private import semmle.python.ApiGraphs + +/** + * Provides models for the `ujson` PyPI package. + * See https://pypi.org/project/ujson/. + */ +private module UjsonModel { + /** + * A call to `usjon.dumps` or `ujson.encode`. + */ + private class UjsonDumpsCall extends Encoding::Range, DataFlow::CallCfgNode { + UjsonDumpsCall() { this = API::moduleImport("ujson").getMember(["dumps", "encode"]).getACall() } + + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + } + + /** + * A call to `ujson.dump`. + */ + private class UjsonDumpCall extends Encoding::Range, DataFlow::CallCfgNode { + UjsonDumpCall() { this = API::moduleImport("ujson").getMember("dump").getACall() } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } + + override DataFlow::Node getOutput() { + result.(DataFlow::PostUpdateNode).getPreUpdateNode() = this.getArg(1) + } + + override string getFormat() { result = "JSON" } + } + + /** + * A call to `ujson.loads` or `ujson.decode`. + */ + private class UjsonLoadsCall extends Decoding::Range, DataFlow::CallCfgNode { + UjsonLoadsCall() { this = API::moduleImport("ujson").getMember(["loads", "decode"]).getACall() } + + // Note: Most other JSON libraries allow the keyword argument `s`, but as of version + // 4.0.2 `ujson` uses `obj` instead. + override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("obj")] } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + + override predicate mayExecuteInput() { none() } + } + + /** + * A call to `ujson.load`. + */ + private class UjsonLoadCall extends Decoding::Range, DataFlow::CallCfgNode { + UjsonLoadCall() { this = API::moduleImport("ujson").getMember("load").getACall() } + + override DataFlow::Node getAnInput() { result = this.getArg(0) } + + override DataFlow::Node getOutput() { result = this } + + override string getFormat() { result = "JSON" } + + override predicate mayExecuteInput() { none() } + } +} diff --git a/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected new file mode 100644 index 00000000000..79d760d87f4 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected @@ -0,0 +1,3 @@ +argumentToEnsureNotTaintedNotMarkedAsSpurious +untaintedArgumentToEnsureTaintedNotMarkedAsMissing +failures diff --git a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql new file mode 100644 index 00000000000..027ad8667be --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql @@ -0,0 +1 @@ +import experimental.meta.InlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/ujson/taint_test.py b/python/ql/test/library-tests/frameworks/ujson/taint_test.py new file mode 100644 index 00000000000..2c965518393 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/ujson/taint_test.py @@ -0,0 +1,44 @@ +import ujson +from io import StringIO + +def test(): + ts = TAINTED_STRING + tainted_obj = {"foo": ts} + + encoded = ujson.dumps(tainted_obj) # $ encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + + ensure_tainted( + encoded, # $ tainted + ujson.dumps(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + ujson.dumps(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + ujson.loads(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + ujson.loads(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + + ujson.encode(tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + ujson.encode(obj=tainted_obj), # $ tainted encodeOutput=Attribute() encodeFormat=JSON encodeInput=tainted_obj + ujson.decode(encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + ujson.decode(obj=encoded), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=encoded + ) + + # load/dump with file-like + tainted_filelike = StringIO() + ujson.dump(tainted_obj, tainted_filelike) # $ encodeFormat=JSON encodeInput=tainted_obj + + tainted_filelike.seek(0) + ensure_tainted( + tainted_filelike, # $ MISSING: tainted + ujson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted + ) + + # load/dump with file-like using keyword-args does not work in `ujson` + + +# To make things runable + +TAINTED_STRING = "TAINTED_STRING" +def ensure_tainted(*args): + print("- ensure_tainted") + for i, arg in enumerate(args): + print("arg {}: {!r}".format(i, arg)) + +test() From c0b65314be2a83d28ac441bab610a4ea00a12e17 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 15:18:42 +0200 Subject: [PATCH 299/550] C++: Fix false positive by restricting _both_ the old (unconverted) expression _and_ all of the conversions. --- cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql | 3 +++ .../ComparisonWithWiderType/ComparisonWithWiderType.expected | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 8c33f78316a..1053b5041c8 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -49,6 +49,9 @@ where small = rel.getLesserOperand() and large = rel.getGreaterOperand() and rel = l.getCondition().getAChild*() and + forall(Expr conv | conv = large.getConversion*() | + upperBound(conv).log2() > getComparisonSize(small) * 8 + ) and upperBound(large.getFullyConverted()).log2() > getComparisonSize(small) * 8 and // Ignore cases where the smaller type is int or larger // These are still bugs, but you should need a very large string or array to diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected index 652a34f98f1..d04bff0a812 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected @@ -1,4 +1,3 @@ -| test3.cpp:6:8:6:71 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type int. | test3.cpp:5:34:5:38 | small | small | test3.cpp:6:42:6:70 | ... - ... | ... - ... | | test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x | | test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x | | test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x | From 51d04cb5b333e55bb91a848e68f958b8744959e2 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 15:30:35 +0200 Subject: [PATCH 300/550] C++: Correct test annotation. --- .../CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp index cbd9c2e5bd9..2cde5e689c7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp @@ -3,5 +3,5 @@ void test_issue_5850(unsigned char small, unsigned int large1) { } void test_widening(unsigned char small, char large) { - for(; small < static_cast(static_cast(large) - 1); small++) { } // GOOD [FALSE POSITIVE] + for(; small < static_cast(static_cast(large) - 1); small++) { } // GOOD } From d55db836cb4aa8ae65eb1d6046bd0547dd52b919 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 10 May 2021 16:13:54 +0200 Subject: [PATCH 301/550] C++: Remove implied conjunct. --- cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 1053b5041c8..2d21ed8a3b2 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -52,7 +52,6 @@ where forall(Expr conv | conv = large.getConversion*() | upperBound(conv).log2() > getComparisonSize(small) * 8 ) and - upperBound(large.getFullyConverted()).log2() > getComparisonSize(small) * 8 and // Ignore cases where the smaller type is int or larger // These are still bugs, but you should need a very large string or array to // trigger them. We will want to disable this for some applications, but it's From 1b0d5053e7a7a1fccaa463761fc8d7a5e142f7a5 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Mon, 10 May 2021 16:21:29 +0200 Subject: [PATCH 302/550] Python: simplejson load/dump only works with lib installed Which I had done locally. Problem is the same about not having PostUpdateNode when points-to is not able to resolve the call, so I'm happy to just make CI happy right now, and hopefully we'll get a fix to the underlying problem soon :blush: --- .../library-tests/frameworks/simplejson/taint_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py index 2b7de24a0c2..6f5d45b0b33 100644 --- a/python/ql/test/library-tests/frameworks/simplejson/taint_test.py +++ b/python/ql/test/library-tests/frameworks/simplejson/taint_test.py @@ -21,8 +21,8 @@ def test(): tainted_filelike.seek(0) ensure_tainted( - tainted_filelike, # $ tainted - simplejson.load(tainted_filelike), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike + tainted_filelike, # $ MISSING: tainted + simplejson.load(tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted ) # load/dump with file-like using keyword-args @@ -31,8 +31,8 @@ def test(): tainted_filelike.seek(0) ensure_tainted( - tainted_filelike, # $ tainted - simplejson.load(fp=tainted_filelike), # $ tainted decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike + tainted_filelike, # $ MISSING: tainted + simplejson.load(fp=tainted_filelike), # $ decodeOutput=Attribute() decodeFormat=JSON decodeInput=tainted_filelike MISSING: tainted ) # To make things runable From 31ac6442e8c3161405938f8621d6c15b34439df8 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 10 May 2021 15:49:31 +0200 Subject: [PATCH 303/550] C#: Fix default parameter value generation in case of error symbols --- .../extractor/Semmle.Extraction.CSharp/Entities/Expression.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index 103ea8cacda..a478047ac7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -170,7 +170,8 @@ namespace Semmle.Extraction.CSharp.Entities public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location) { - if (!parameter.HasExplicitDefaultValue) + if (!parameter.HasExplicitDefaultValue || + parameter.Type is IErrorTypeSymbol) { return null; } From dd86da3f242ea1186f85be6da8e424940b91fe02 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 10 May 2021 15:48:33 +0200 Subject: [PATCH 304/550] C#: Remove base class from type IDs in trap files --- .../Entities/Types/Type.cs | 74 +- .../SymbolExtensions.cs | 47 +- csharp/ql/src/semmlecode.csharp.dbscheme | 2 +- .../old.dbscheme | 2083 +++++++++++++++++ .../semmlecode.csharp.dbscheme | 2083 +++++++++++++++++ .../upgrade.properties | 2 + 6 files changed, 4233 insertions(+), 58 deletions(-) create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme create mode 100644 csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index e2522648678..67936f9a913 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -81,22 +81,45 @@ namespace Semmle.Extraction.CSharp.Entities Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType); trapFile.WriteLine("\")"); + var baseTypes = GetBaseTypeDeclarations(); + // Visit base types - var baseTypes = new List(); if (Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base) { - var baseKey = Create(Context, @base); - trapFile.extend(this, baseKey.TypeRef); - if (Symbol.TypeKind != TypeKind.Struct) - baseTypes.Add(baseKey); + var bts = GetBaseTypeDeclarations(baseTypes, @base); + + Context.PopulateLater(() => + { + var baseKey = Create(Context, @base); + trapFile.extend(this, baseKey.TypeRef); + + if (Symbol.TypeKind != TypeKind.Struct) + { + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, baseKey); + } + } + }); } + // Visit implemented interfaces if (!(base.Symbol is IArrayTypeSymbol)) { - foreach (var t in base.Symbol.Interfaces.Select(i => Create(Context, i))) + foreach (var i in base.Symbol.Interfaces) { - trapFile.implement(this, t.TypeRef); - baseTypes.Add(t); + var bts = GetBaseTypeDeclarations(baseTypes, i); + + Context.PopulateLater(() => + { + var interfaceKey = Create(Context, i); + trapFile.implement(this, interfaceKey.TypeRef); + + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, interfaceKey); + } + }); } } @@ -145,23 +168,30 @@ namespace Semmle.Extraction.CSharp.Entities } Modifier.ExtractModifiers(Context, trapFile, this, Symbol); + } - if (IsSourceDeclaration && Symbol.FromSource()) + private IEnumerable GetBaseTypeDeclarations() + { + if (!IsSourceDeclaration || !Symbol.FromSource()) { - var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); - - var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - - baseLists - .Where(bl => bl is not null) - .SelectMany(bl => bl!.Types) - .Zip( - baseTypes.Where(bt => bt.Symbol.SpecialType != SpecialType.System_Object), - (s, t) => TypeMention.Create(Context, s.Type, this, t)) - .Enumerate(); + return Enumerable.Empty(); } + + var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); + + var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + + return baseLists + .Where(bl => bl is not null) + .SelectMany(bl => bl!.Types) + .ToList(); + } + + private IEnumerable GetBaseTypeDeclarations(IEnumerable baseTypes, INamedTypeSymbol type) + { + return baseTypes.Where(bt => SymbolEqualityComparer.Default.Equals(Context.GetModel(bt).GetTypeInfo(bt.Type).Type, type)); } private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action storeReturnType) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 32b90e37068..91f0d0edf20 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -121,8 +121,6 @@ namespace Semmle.Extraction.CSharp named = named.TupleUnderlyingType; if (IdDependsOnImpl(named.ContainingType)) return true; - if (IdDependsOnImpl(named.GetNonObjectBaseType(cx))) - return true; if (IdDependsOnImpl(named.ConstructedFrom)) return true; return named.TypeArguments.Any(IdDependsOnImpl); @@ -160,10 +158,7 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => - type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType); - - private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) { using (cx.StackGuard) { @@ -171,7 +166,7 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -181,16 +176,16 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('_'); trapFile.Write(tp.Name); return; @@ -207,7 +202,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) { if (symbol is null) { @@ -232,7 +227,7 @@ namespace Semmle.Extraction.CSharp if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined)) trapFile.Write("__self__"); else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined)) - type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + type.BuildTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType) trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType)); else @@ -287,7 +282,7 @@ namespace Semmle.Extraction.CSharp BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined)); } - private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (!constructUnderlyingTupleType && named.IsTupleType) { @@ -297,7 +292,7 @@ namespace Semmle.Extraction.CSharp { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined); } ); trapFile.Write(")"); @@ -308,7 +303,7 @@ namespace Semmle.Extraction.CSharp { if (named.ContainingType is not null) { - named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); trapFile.Write('.'); } else if (named.ContainingNamespace is not null) @@ -333,35 +328,17 @@ namespace Semmle.Extraction.CSharp } else { - named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); trapFile.Write('<'); // Encode the nullability of the type arguments in the label. // Type arguments with different nullability can result in // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined) ); trapFile.Write('>'); } - - if (addBaseClass && named.GetNonObjectBaseType(cx) is INamedTypeSymbol @base) - { - // We need to limit unfolding of base classes. For example, in - // - // ```csharp - // class C1 { } - // class C2 : C1> { } - // class C3 : C1> { } - // class C4 : C3 { } - // ``` - // - // when we generate the label for `C4`, the base class `C3` has itself `C1>` as - // a base class, which in turn has `C1>` as a base class. The latter has the original - // base class `C3` as a type argument, which would lead to infinite unfolding. - trapFile.Write(" : "); - @base.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass: false); - } } private static void BuildNamespace(this INamespaceSymbol ns, Context cx, EscapingTextWriter trapFile) diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme b/csharp/ql/src/semmlecode.csharp.dbscheme index 9258e9b38d8..770f844243d 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme +++ b/csharp/ql/src/semmlecode.csharp.dbscheme @@ -529,7 +529,7 @@ function_pointer_return_type( int return_type_id: @type_or_ref ref); extend( - unique int sub: @type ref, + int sub: @type ref, int super: @type_or_ref ref); anonymous_types( diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme new file mode 100644 index 00000000000..9258e9b38d8 --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + unique int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme new file mode 100644 index 00000000000..770f844243d --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties new file mode 100644 index 00000000000..64b44aecc3c --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties @@ -0,0 +1,2 @@ +description: Removed unique base class constraint +compatibility: backwards From d27316eb3e7227930e966d7eba441496e4281dad Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 10 May 2021 11:55:31 -0400 Subject: [PATCH 305/550] Apply suggestions from code review Co-authored-by: Marcono1234 --- java/change-notes/2021-05-05-kryo-improvements.md | 2 +- java/ql/src/semmle/code/java/frameworks/Kryo.qll | 2 +- java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md index f8ba79eb863..5b39d1daae0 100644 --- a/java/change-notes/2021-05-05-kryo-improvements.md +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -1,3 +1,3 @@ lgtm,codescanning * Add support for version 5 of the Kryo serialzation/deserialization framework. -* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder. (#4992)[https://github.com/github/codeql/issues/4992] +* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992) diff --git a/java/ql/src/semmle/code/java/frameworks/Kryo.qll b/java/ql/src/semmle/code/java/frameworks/Kryo.qll index 16873fc751f..317148d56b5 100644 --- a/java/ql/src/semmle/code/java/frameworks/Kryo.qll +++ b/java/ql/src/semmle/code/java/frameworks/Kryo.qll @@ -47,7 +47,7 @@ class KryoPoolBuilder extends RefType { } /** - * A Kryo pool builder method used a fluent API call chain. + * A Kryo pool builder method used in a fluent API call chain. */ class KryoPoolBuilderMethod extends Method { KryoPoolBuilderMethod() { diff --git a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll index def37c0964e..a1561c0e6cd 100644 --- a/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll +++ b/java/ql/src/semmle/code/java/security/UnsafeDeserialization.qll @@ -57,7 +57,7 @@ class SafeKryo extends DataFlow2::Configuration { } /** - * Holds when a funcitonal expression is used to create a `KryoPool.Builder`. + * Holds when a functional expression is used to create a `KryoPool.Builder`. * Eg. `new KryoPool.Builder(() -> new Kryo())` */ private predicate stepKryoPoolBuilderFactoryArgToConstructor( From 2e098f050e07a5647001e98694b83a13470b74e7 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 10 May 2021 18:33:07 +0200 Subject: [PATCH 306/550] Java: Ignore char array based closeables for CloseReader.ql and CloseWriter.ql --- java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp | 2 +- java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql | 7 ++----- java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp | 2 +- java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql | 8 ++------ 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp index b0ded8e53a1..e5574bfc179 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp @@ -14,7 +14,7 @@ but not closed may cause a resource leak.

    Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions, it is safest to close a resource in a finally block. (However, this is unnecessary for -subclasses of StringReader and ByteArrayInputStream.) +subclasses of CharArrayReader, StringReader and ByteArrayInputStream.)

    For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql index 9d62237bd71..5993bd1de2b 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql @@ -17,17 +17,14 @@ import CloseType predicate readerType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("Reader") or - sup.hasName("InputStream") or + sup.hasName(["Reader", "InputStream"]) or sup.hasQualifiedName("java.util.zip", "ZipFile") ) } predicate safeReaderType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("StringReader") or - sup.hasName("ByteArrayInputStream") or - sup.hasName("StringInputStream") + sup.hasName(["CharArrayReader", "StringReader", "ByteArrayInputStream"]) ) } diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp index 0b348a3f9b8..84a50f914f7 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp @@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.

    Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions, it is safest to close a resource properly in a finally block. (However, this is unnecessary for -subclasses of StringWriter and ByteArrayOutputStream.)

    +subclasses of CharArrayWriter, StringWriter and ByteArrayOutputStream.)

    For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable is to declare them within a try-with-resources statement, so that they are closed implicitly.

    diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql index 113f3bd3267..824951f86f5 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql @@ -16,16 +16,12 @@ import CloseType predicate writerType(RefType t) { - exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("Writer") or - sup.hasName("OutputStream") - ) + exists(RefType sup | sup = t.getASupertype*() | sup.hasName(["Writer", "OutputStream"])) } predicate safeWriterType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("StringWriter") or - sup.hasName("ByteArrayOutputStream") + sup.hasName(["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"]) ) } From f85aff869c0bc802d17bdc1622646722fa7a281f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 10 May 2021 16:37:23 -0400 Subject: [PATCH 307/550] Java: Fix PR feedback --- java/ql/src/semmle/code/FileSystem.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/FileSystem.qll b/java/ql/src/semmle/code/FileSystem.qll index 708348b85f5..986677dd2f0 100755 --- a/java/ql/src/semmle/code/FileSystem.qll +++ b/java/ql/src/semmle/code/FileSystem.qll @@ -148,8 +148,8 @@ class Container extends @container, Top { /** * Gets a textual representation of this container. * - * The default implementation returns the absolute path to the container, but subclasses may - * may override to provide a different result. To get the absolute path for any `Container`, call + * The default implementation gets the absolute path to the container, but subclasses may override + * to provide a different result. To get the absolute path of any `Container`, call * `Container.getAbsolutePath()` directly. */ override string toString() { result = getAbsolutePath() } From 54f191cfe37e136bcc7189b2fb01f44dbb01758b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:23:03 +0200 Subject: [PATCH 308/550] add support for rejected promise values in API graphs --- .../ql/src/semmle/javascript/ApiGraphs.qll | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/ApiGraphs.qll b/javascript/ql/src/semmle/javascript/ApiGraphs.qll index 385dc1e76d3..590142590a4 100644 --- a/javascript/ql/src/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/src/semmle/javascript/ApiGraphs.qll @@ -183,6 +183,11 @@ module API { */ Node getPromised() { result = getASuccessor(Label::promised()) } + /** + * Gets a node representing the error wrapped in the `Promise` object represented by this node. + */ + Node getPromisedError() { result = getASuccessor(Label::promisedError()) } + /** * Gets a string representation of the lexicographically least among all shortest access paths * from the root to this node. @@ -468,6 +473,9 @@ module API { or lbl = Label::promised() and PromiseFlow::storeStep(rhs, pred, Promises::valueProp()) + or + lbl = Label::promisedError() and + PromiseFlow::storeStep(rhs, pred, Promises::errorProp()) ) or exists(DataFlow::ClassNode cls, string name | @@ -482,6 +490,12 @@ module API { rhs = f.getAReturn() ) or + exists(DataFlow::FunctionNode f | + base = MkAsyncFuncResult(f) and + lbl = Label::promisedError() and + rhs = f.getExceptionalReturn() + ) + or exists(int i | lbl = Label::parameter(i) and argumentPassing(base, i, rhs) @@ -559,6 +573,9 @@ module API { or lbl = Label::promised() and PromiseFlow::loadStep(pred, ref, Promises::valueProp()) + or + lbl = Label::promisedError() and + PromiseFlow::loadStep(pred, ref, Promises::errorProp()) ) or exists(DataFlow::Node def, DataFlow::FunctionNode fn | @@ -962,6 +979,9 @@ private module Label { /** Gets the `promised` edge label connecting a promise to its contained value. */ string promised() { result = "promised" } + + /** Gets the `promisedError` edge label connecting a promise to its rejected value. */ + string promisedError() { result = "promisedError" } } private class NodeModuleSourcesNodes extends DataFlow::SourceNode::Range { From 52991dc4a1ec07410fa2818776f756c12418f707 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:23:51 +0200 Subject: [PATCH 309/550] rewrite the axios model to use API graphs --- .../javascript/frameworks/ClientRequests.qll | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 451ad3f24d2..78f7ec92a16 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -206,19 +206,14 @@ module ClientRequest { /** * A model of a URL request made using the `axios` library. */ - class AxiosUrlRequest extends ClientRequest::Range { + class AxiosUrlRequest extends ClientRequest::Range, API::CallNode { string method; AxiosUrlRequest() { - exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() | - moduleName = "axios" and - ( - callee = DataFlow::moduleImport(moduleName) and method = "request" - or - callee = DataFlow::moduleMember(moduleName, method) and - (method = httpMethodName() or method = "request") - ) - ) + this = API::moduleImport("axios").getACall() and method = "request" + or + this = API::moduleImport("axios").getMember(method).getACall() and + method = [httpMethodName(), "request"] } private int getOptionsArgIndex() { @@ -247,12 +242,10 @@ module ClientRequest { method = "request" and result = getOptionArgument(0, "data") or - (method = "post" or method = "put" or method = "put") and - (result = getArgument(1) or result = getOptionArgument(2, "data")) + method = ["post", "put", "put"] and + result = [getArgument(1), getOptionArgument(2, "data")] or - exists(string name | name = "headers" or name = "params" | - result = getOptionArgument([0 .. 2], name) - ) + result = getOptionArgument([0 .. 2], ["headers", "params"]) } /** Gets the response type from the options passed in. */ From 99e98419dc3e38c06c2cf6ec6136d88efeae7af6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 11 May 2021 11:24:21 +0200 Subject: [PATCH 310/550] add support for error values in an axios client request --- .../semmle/javascript/frameworks/ClientRequests.qll | 5 +++++ .../ClientRequests/ClientRequests.expected | 5 +++++ .../library-tests/frameworks/ClientRequests/tst.js | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 78f7ec92a16..477f9354d03 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -268,6 +268,11 @@ module ClientRequest { responseType = getResponseType() and promise = true and result = this + or + responseType = getResponseType() and + promise = false and + result = + getReturn().getPromisedError().getMember("response").getMember("data").getAnImmediateUse() } } diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index e448b888e12..c6542eb009d 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -87,6 +87,7 @@ test_ClientRequest | tst.js:271:3:271:61 | proxy.w ... 080' }) | | tst.js:274:1:283:2 | httpPro ... true\\n}) | | tst.js:286:20:286:55 | new Web ... :8080') | +| tst.js:296:5:299:6 | axios({ ... \\n }) | test_getADataNode | tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data | | tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 | @@ -227,6 +228,8 @@ test_getUrl | tst.js:271:3:271:61 | proxy.w ... 080' }) | tst.js:271:33:271:58 | 'http:/ ... m:8080' | | tst.js:274:1:283:2 | httpPro ... true\\n}) | tst.js:275:13:281:5 | {\\n ... ,\\n } | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:286:34:286:54 | 'ws://l ... t:8080' | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:11:299:5 | {\\n ... ,\\n } | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:298:14:298:44 | "http:/ ... -axios" | test_getAResponseDataNode | tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true | | tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true | @@ -294,3 +297,5 @@ test_getAResponseDataNode | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:67:235:70 | resp | fetch.response | false | | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:5:299:6 | axios({ ... \\n }) | json | true | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:42 | err.response.data | json | false | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index bc15565c072..f284ffaa407 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -290,4 +290,17 @@ function webSocket() { socket.addEventListener('message', function (event) { console.log("Data from server: " + event.data); }); +} + +function moreAxios() { + axios({ + method: 'GET', + url: "http://example.org/more-axios", + }).then( + x => res.send(x.data), + (err) => { + const status = err.response.status; + const data = err.response.data; + } + ); } \ No newline at end of file From 717070c7e439a1d99512a1f4238d157daef4cbc3 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 11 May 2021 13:11:35 +0200 Subject: [PATCH 311/550] Fix/cleanup passed and default arguments values --- .../Entities/Types/FunctionPointerType.cs | 2 +- .../Entities/Types/TupleType.cs | 2 +- .../SymbolExtensions.cs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs index 37ba909ec46..4c3ab516172 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";functionpointertype"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index ddbf1ac52a6..56db07671d7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";tuple"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 91f0d0edf20..0bda977a6d5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -158,7 +158,7 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { using (cx.StackGuard) { @@ -166,7 +166,7 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -180,12 +180,12 @@ namespace Semmle.Extraction.CSharp return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('_'); trapFile.Write(tp.Name); return; @@ -202,7 +202,7 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (symbol is null) { @@ -245,7 +245,7 @@ namespace Semmle.Extraction.CSharp /// . ///
    public static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) => - symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); + symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); /// /// Constructs an array suffix string for this array type symbol. @@ -292,7 +292,7 @@ namespace Semmle.Extraction.CSharp { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); } ); trapFile.Write(")"); @@ -303,7 +303,7 @@ namespace Semmle.Extraction.CSharp { if (named.ContainingType is not null) { - named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined); + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('.'); } else if (named.ContainingNamespace is not null) @@ -335,7 +335,7 @@ namespace Semmle.Extraction.CSharp // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false) ); trapFile.Write('>'); } From 24d8abd2c2337aa21c048fe4f1cffed5b55cb41a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:27:53 +0200 Subject: [PATCH 312/550] C++: Add false positive testcase when an absolute value is used in comparison. --- .../semmle/tainted/ArithmeticTainted.expected | 4 ++++ .../Security/CWE/CWE-190/semmle/tainted/test5.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index bdf00e0a5df..ee55beac3a2 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -3,6 +3,10 @@ | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | +| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index 527c603d1b8..de0baa93bb1 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -18,3 +18,15 @@ void useTaintedInt() y = getTaintedInt(); y = y * 1024; // BAD: arithmetic on a tainted value } + +typedef long long int intmax_t; + +intmax_t imaxabs(intmax_t j); + +void useTaintedIntWithGuard() { + int tainted = getTaintedInt(); + + if(imaxabs(tainted) <= 100) { + int product = tainted * tainted; // GOOD: can't underflow/overflow [FALSE POSITIVE] + } +} \ No newline at end of file From 0d9a85ca6b30511e41c1924161fae2193cec3b71 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 11 May 2021 08:29:50 -0400 Subject: [PATCH 313/550] Update java/change-notes/2021-05-05-kryo-improvements.md Co-authored-by: Anders Schack-Mulligen --- java/change-notes/2021-05-05-kryo-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md index 5b39d1daae0..dbacb10099b 100644 --- a/java/change-notes/2021-05-05-kryo-improvements.md +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -1,3 +1,3 @@ lgtm,codescanning -* Add support for version 5 of the Kryo serialzation/deserialization framework. +* Add support for version 5 of the Kryo serialization/deserialization framework. * Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992) From 48e783184cd82692208d21d6286deeb38924804f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:30:28 +0200 Subject: [PATCH 314/550] C++: Fix false positive by recognizing more absolute value functions in Overflow.qll --- cpp/ql/src/semmle/code/cpp/security/Overflow.qll | 2 +- .../CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected | 4 ---- .../query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index 6cf82791d52..b8ed406cb4a 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -12,7 +12,7 @@ import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils * Holds if the value of `use` is guarded using `abs`. */ predicate guardedAbs(Operation e, Expr use) { - exists(FunctionCall fc | fc.getTarget().getName() = "abs" | + exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] | fc.getArgument(0).getAChild*() = use and guardedLesser(e, fc) ) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index ee55beac3a2..bdf00e0a5df 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -3,10 +3,6 @@ | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:17:30:23 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | -| test5.cpp:30:27:30:33 | tainted | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value | | test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index de0baa93bb1..92eb71ad541 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -27,6 +27,6 @@ void useTaintedIntWithGuard() { int tainted = getTaintedInt(); if(imaxabs(tainted) <= 100) { - int product = tainted * tainted; // GOOD: can't underflow/overflow [FALSE POSITIVE] + int product = tainted * tainted; // GOOD: can't underflow/overflow } } \ No newline at end of file From d66506b0a388f40634f80318a4e8faedcf5909e7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 11 May 2021 14:40:10 +0200 Subject: [PATCH 315/550] Data flow: Rename `{Argument,Parameter}NodeExt` to `{Arg,Param}Node` --- .../cpp/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../dataflow/internal/DataFlowImplLocal.qll | 112 ++++++++---------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../ir/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../csharp/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImpl5.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../java/dataflow/internal/DataFlowImpl.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl5.qll | 112 ++++++++---------- .../java/dataflow/internal/DataFlowImpl6.qll | 112 ++++++++---------- .../dataflow/internal/DataFlowImplCommon.qll | 94 +++++++-------- .../dataflow/new/internal/DataFlowImpl.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl2.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl3.qll | 112 ++++++++---------- .../dataflow/new/internal/DataFlowImpl4.qll | 112 ++++++++---------- .../new/internal/DataFlowImplCommon.qll | 94 +++++++-------- 29 files changed, 1372 insertions(+), 1786 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index cf83530bcbd..058d66b1496 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index cf83530bcbd..058d66b1496 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index cf83530bcbd..058d66b1496 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -385,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -512,24 +512,22 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, Configuration config - ) { - exists(ParameterNodeExt p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNodeExt arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } @@ -594,9 +592,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -662,7 +658,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -674,7 +670,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -734,8 +730,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNodeExt arg, ParameterNodeExt p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -943,10 +938,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -991,7 +986,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1132,10 +1127,9 @@ private module Stage2 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1145,7 +1139,7 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1198,15 +1192,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1246,8 +1238,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1276,7 +1267,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNodeExt or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1586,10 +1577,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1634,7 +1625,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1775,10 +1766,9 @@ private module Stage3 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -1788,7 +1778,7 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -1841,15 +1831,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2160,8 +2148,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNodeExt node1, ParameterNodeExt node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2305,10 +2292,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNodeExt p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNodeExt arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2353,7 +2340,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2494,10 +2481,9 @@ private module Stage4 { pragma[nomagic] private predicate revFlowIn( - DataFlowCall call, ArgumentNodeExt arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config + DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config ) { - exists(ParameterNodeExt p, boolean allowsFieldFlow | + exists(ParamNode p, boolean allowsFieldFlow | revFlow(p, toReturn, returnAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) | @@ -2507,7 +2493,7 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNodeExt arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { revFlowIn(call, arg, true, apSome(returnAp), ap, config) } @@ -2560,15 +2546,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNodeExt p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough( - ParameterNodeExt p, DataFlowCallable c, Ap ap, Configuration config - ) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2613,7 +2597,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNodeExt p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2634,7 +2618,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNodeExt p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -3242,7 +3226,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3255,7 +3239,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNodeExt p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3279,7 +3263,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3575,7 +3559,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNodeExt p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3931,7 +3915,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNodeExt arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3950,7 +3934,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNodeExt p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3987,7 +3971,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNodeExt p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4044,7 +4028,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNodeExt p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4122,7 +4106,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNodeExt p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4145,7 +4129,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNodeExt node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index b5ceabc605d..b8e08408911 100644 --- a/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -35,24 +35,22 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda( - DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg - ) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -120,7 +118,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if castNode(node) or node instanceof ArgumentNodeExt or node instanceof ReturnNode + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -178,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNodeExt p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -229,8 +227,8 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNodeExt p, DataFlowType t, - boolean toJump, DataFlowCallOption lastCall + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) } @@ -276,7 +274,7 @@ private module Cached { predicate outNodeExt(Node n) { n instanceof OutNode or - n.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNodeExt + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode } cached @@ -286,7 +284,7 @@ private module Cached { OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { result = getAnOutNode(call, k.(ValueReturnKind).getKind()) or - exists(ArgumentNodeExt arg | + exists(ArgNode arg | result.(PostUpdateNode).getPreUpdateNode() = arg and arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) ) @@ -296,7 +294,7 @@ private module Cached { predicate returnNodeExt(Node n, ReturnKindExt k) { k = TValueReturn(n.(ReturnNode).getKind()) or - exists(ParameterNodeExt p, int pos | + exists(ParamNode p, int pos | parameterValueFlowsToPreUpdate(p, n) and p.isParameterOf(_, pos) and k = TParamUpdate(pos) @@ -309,7 +307,7 @@ private module Cached { cached predicate castingNode(Node n) { castNode(n) or - n instanceof ParameterNodeExt or + n instanceof ParamNode or n instanceof OutNodeExt or // For reads, `x.f`, we want to check that the tracked type after the read (which // is obtained by popping the head of the access path stack) is compatible with @@ -346,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNodeExt p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -355,7 +353,7 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNodeExt p, ArgumentNodeExt arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and @@ -397,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNodeExt p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -415,27 +413,25 @@ private module Cached { ) or // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand( - ParameterNodeExt p, ArgumentNodeExt arg, boolean read - ) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNodeExt p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -447,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNodeExt p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -456,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -469,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNodeExt arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNodeExt p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -503,7 +499,7 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then @@ -517,7 +513,7 @@ private module Cached { } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNodeExt p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -542,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNodeExt p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNodeExt arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNodeExt p, ArgumentNodeExt arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNodeExt arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNodeExt param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -583,7 +577,7 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNodeExt arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) @@ -603,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNodeExt arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -616,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNodeExt p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -722,7 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - private predicate parameterValueFlowsToPreUpdate(ParameterNodeExt p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -778,8 +772,8 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNodeExt).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNodeExt), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) @@ -827,7 +821,7 @@ private module Cached { cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNodeExt p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -942,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNodeExt p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -1005,8 +999,8 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) * The value of a parameter at function entry, viewed as a node in a data * flow graph. */ -class ParameterNodeExt extends Node { - ParameterNodeExt() { parameterNode(this, _, _) } +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } /** * Holds if this node is the parameter of callable `c` at the specified @@ -1016,8 +1010,8 @@ class ParameterNodeExt extends Node { } /** A data-flow node that represents a call argument. */ -class ArgumentNodeExt extends Node { - ArgumentNodeExt() { argumentNode(this, _, _) } +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } /** Holds if this argument occurs at the given position in the given call. */ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } From 3e21f479a99e3bd5c85299755cc632c1c9da5383 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 14:58:48 +0200 Subject: [PATCH 316/550] C++: Add change-note. --- cpp/change-notes/2021-03-11-overflow-abs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-03-11-overflow-abs.md diff --git a/cpp/change-notes/2021-03-11-overflow-abs.md b/cpp/change-notes/2021-03-11-overflow-abs.md new file mode 100644 index 00000000000..66854412f72 --- /dev/null +++ b/cpp/change-notes/2021-03-11-overflow-abs.md @@ -0,0 +1,2 @@ +lgtm +* The `cpp/tainted-arithmetic`, `cpp/arithmetic-with-extreme-values`, and `cpp/uncontrolled-arithmetic` queries now recognize more functions as returning the absolute value of their input. As a result, they produce fewer false positives. From 56b1f15ddaaeb5b53267293933b3850058678f6c Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Fri, 30 Apr 2021 19:18:35 -0400 Subject: [PATCH 317/550] [Java] Add taint tracking through Jackson deserialization --- .../jackson/JacksonSerializability.qll | 19 ++++++++ .../dataflow/taint-jackson/Test.java | 38 +++++++++++++-- .../dataflow/taint-jackson/dataFlow.expected | 48 ------------------- .../dataflow/taint-jackson/dataFlow.ql | 29 ++++++++--- .../jackson/databind/ObjectMapper.java | 4 ++ .../jackson/databind/ObjectReader.java | 45 +++++++++++++++++ 6 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 3356e31d965..c028af71d8d 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -50,6 +50,15 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { } } +library class JacksonReadValueMethod extends Method, TaintPreservingCallable { + JacksonReadValueMethod() { + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and + hasName("readValue") + } + + override predicate returnsTaintFrom(int arg) { arg = 0 } +} + /** A type whose values are explicitly serialized in a call to a Jackson method. */ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { ExplicitlyWrittenJacksonSerializableType() { @@ -135,6 +144,16 @@ class JacksonDeserializableField extends DeserializableField { } } +class JacksonDeserializableFieldAccess extends FieldAccess { + JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } +} + +class JacksonDeseializedTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() + } +} + /** * A call to the `addMixInAnnotations` or `addMixIn` Jackson method. * diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 2caf9e4ee80..16b223ed2e8 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -8,28 +8,44 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.ObjectReader; class Test { + public static class Potato { + private String name; + + private String getName() { + return name; + } + } + public static String taint() { return "tainted"; } + public static void sink(Object any) {} + public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException { String s = taint(); ObjectMapper om = new ObjectMapper(); File file = new File("testFile"); om.writeValue(file, s); + sink(file); //$hasTaintFlow OutputStream out = new FileOutputStream(file); om.writeValue(out, s); + sink(file); //$hasTaintFlow Writer writer = new StringWriter(); om.writeValue(writer, s); + sink(writer); //$hasTaintFlow JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter()); om.writeValue(generator, s); + sink(generator); //$hasTaintFlow String t = om.writeValueAsString(s); - System.out.println(t); + sink(t); //$hasTaintFlow byte[] bs = om.writeValueAsBytes(s); String reconstructed = new String(bs, "utf-8"); - System.out.println(reconstructed); + sink(bs); //$hasTaintFlow + sink(reconstructed); //$hasTaintFlow } public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException { @@ -37,16 +53,30 @@ class Test { ObjectWriter ow = new ObjectWriter(); File file = new File("testFile"); ow.writeValue(file, s); + sink(file); //$hasTaintFlow OutputStream out = new FileOutputStream(file); ow.writeValue(out, s); + sink(out); //$hasTaintFlow Writer writer = new StringWriter(); ow.writeValue(writer, s); + sink(writer); //$hasTaintFlow JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter()); ow.writeValue(generator, s); + sink(generator); //$hasTaintFlow String t = ow.writeValueAsString(s); - System.out.println(t); + sink(t); //$hasTaintFlow byte[] bs = ow.writeValueAsBytes(s); String reconstructed = new String(bs, "utf-8"); - System.out.println(reconstructed); + sink(bs); //$hasTaintFlow + sink(reconstructed); //$hasTaintFlow + } + + public static void jacksonObjectReader() throws java.io.IOException { + String s = taint(); + ObjectMapper om = new ObjectMapper(); + ObjectReader reader = om.readerFor(Potato.class); + sink(reader.readValue(s)); //$hasTaintFlow + sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow + sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow } } diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected index 122b21d50fe..e69de29bb2d 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected @@ -1,48 +0,0 @@ -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value | -| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value | -| Test.java:18:14:18:20 | taint(...) | -| Test.java:21:17:21:20 | file [post update] | -| Test.java:21:23:21:23 | s | -| Test.java:22:43:22:46 | file | -| Test.java:23:17:23:19 | out [post update] | -| Test.java:23:22:23:22 | s | -| Test.java:25:17:25:22 | writer [post update] | -| Test.java:25:25:25:25 | s | -| Test.java:27:17:27:25 | generator [post update] | -| Test.java:27:28:27:28 | s | -| Test.java:28:14:28:37 | writeValueAsString(...) | -| Test.java:28:36:28:36 | s | -| Test.java:29:22:29:22 | t | -| Test.java:30:15:30:37 | writeValueAsBytes(...) | -| Test.java:30:36:30:36 | s | -| Test.java:31:26:31:48 | new String(...) | -| Test.java:31:37:31:38 | bs | -| Test.java:32:22:32:34 | reconstructed | -| Test.java:36:14:36:20 | taint(...) | -| Test.java:39:17:39:20 | file [post update] | -| Test.java:39:23:39:23 | s | -| Test.java:40:43:40:46 | file | -| Test.java:41:17:41:19 | out [post update] | -| Test.java:41:22:41:22 | s | -| Test.java:43:17:43:22 | writer [post update] | -| Test.java:43:25:43:25 | s | -| Test.java:45:17:45:25 | generator [post update] | -| Test.java:45:28:45:28 | s | -| Test.java:46:14:46:37 | writeValueAsString(...) | -| Test.java:46:36:46:36 | s | -| Test.java:47:22:47:22 | t | -| Test.java:48:15:48:37 | writeValueAsBytes(...) | -| Test.java:48:36:48:36 | s | -| Test.java:49:26:49:48 | new String(...) | -| Test.java:49:37:49:38 | bs | -| Test.java:50:22:50:34 | reconstructed | diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql index 333cf485f07..3ee51339b7b 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql @@ -1,17 +1,34 @@ +import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources +import TestUtilities.InlineExpectationsTest class Conf extends TaintTracking::Configuration { Conf() { this = "qltest:dataflow:jackson" } - override predicate isSource(DataFlow::Node source) { - source.asExpr().(MethodAccess).getMethod().hasName("taint") + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("taint") + or + n instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { any() } + override predicate isSink(DataFlow::Node n) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) + } } -from DataFlow::Node source, DataFlow::Node sink, Conf config -where config.hasFlow(source, sink) -select sink +class HasFlowTest extends InlineExpectationsTest { + HasFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = "hasTaintFlow" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java index 455e0c0d309..af6218e4146 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java @@ -26,4 +26,8 @@ public class ObjectMapper { public String writeValueAsString(Object value) { return null; } + + public ObjectReader readerFor(Class type) { + return null; + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java new file mode 100644 index 00000000000..1916730da36 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.databind; + +import java.io.*; + +public class ObjectReader { + public ObjectReader forType(Class valueType) { + return null; + } + + public T readValue(String src) { + return null; + } + + public T readValue(String src, Class valueType) throws IOException { + return null; + } + + public T readValue(byte[] content) throws IOException { + return null; + } + + public T readValue(byte[] content, Class valueType) throws IOException { + return null; + } + + public T readValue(File src) throws IOException { + return null; + } + + public T readValue(InputStream src) throws IOException { + return null; + } + + public T readValue(InputStream src, Class valueType) throws IOException { + return null; + } + + public T readValue(Reader src) throws IOException { + return null; + } + + public T readValue(Reader src, Class valueType) throws IOException { + return null; + } +} \ No newline at end of file From d0638db6e72836399061b506d029ad8a7d0e2f9d Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:02:40 -0400 Subject: [PATCH 318/550] [Java] Add data flow through Iterator deserializers for Jackson --- .../jackson/JacksonSerializability.qll | 31 ++++++++++++---- .../dataflow/taint-jackson/Test.java | 15 ++++++++ .../jackson/databind/MappingIterator.java | 28 ++++++++++++++ .../jackson/databind/ObjectReader.java | 37 +++++++++++++++++++ 4 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index c028af71d8d..1cfe34d7167 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -28,7 +28,7 @@ abstract class JacksonSerializableType extends Type { } * A method used for serializing objects using Jackson. The final parameter is the object to be * serialized. */ -library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { +private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { JacksonWriteValueMethod() { ( getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or @@ -50,17 +50,17 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable { } } -library class JacksonReadValueMethod extends Method, TaintPreservingCallable { +private class JacksonReadValueMethod extends Method, TaintPreservingCallable { JacksonReadValueMethod() { getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and - hasName("readValue") + hasName(["readValue", "readValues"]) } override predicate returnsTaintFrom(int arg) { arg = 0 } } /** A type whose values are explicitly serialized in a call to a Jackson method. */ -library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { +private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType { ExplicitlyWrittenJacksonSerializableType() { exists(MethodAccess ma | // A call to a Jackson write method... @@ -71,8 +71,20 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab } } +/** A type whose values are explicitly deserialized in a call to a Jackson method. */ +private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { + ExplicitlyReadJacksonSerializableType() { + exists(MethodAccess ma | + // A call to a Jackson write method... + ma.getMethod() instanceof JacksonReadValueMethod and + // ...where `this` is used in the final argument, indicating that this type will be deserialized. + usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) + ) + } +} + /** A type used in a `JacksonSerializableField` declaration. */ -library class FieldReferencedJacksonSerializableType extends JacksonSerializableType { +private class FieldReferencedJacksonSerializableType extends JacksonSerializableType { FieldReferencedJacksonSerializableType() { exists(JacksonSerializableField f | usesType(f.getType(), this)) } @@ -105,7 +117,7 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C } /** A type whose values are explicitly deserialized in a call to a Jackson method. */ -library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType { +private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType { ExplicitlyReadJacksonDeserializableType() { exists(TypeLiteralToJacksonDatabindFlowConfiguration conf | usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this) @@ -114,7 +126,7 @@ library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa } /** A type used in a `JacksonDeserializableField` declaration. */ -library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { +private class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { FieldReferencedJacksonDeSerializableType() { exists(JacksonDeserializableField f | usesType(f.getType(), this)) } @@ -144,10 +156,15 @@ class JacksonDeserializableField extends DeserializableField { } } +/** A call to a field that may be deserialized using the Jackson JSON framework. */ class JacksonDeserializableFieldAccess extends FieldAccess { JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } } +/** + * When an object is deserialized by the Jackson JSON framework using a tainted input source, + * the fields that the framework deserialized are themselves tainted input data. + */ class JacksonDeseializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 16b223ed2e8..26f9182339a 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -3,6 +3,7 @@ import java.io.FileOutputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; +import java.util.Iterator; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; @@ -79,4 +80,18 @@ class Test { sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow } + + public static void jacksonObjectReaderIterable() throws java.io.IOException { + String s = taint(); + ObjectMapper om = new ObjectMapper(); + ObjectReader reader = om.readerFor(Potato.class); + sink(reader.readValues(s)); //$hasTaintFlow + Iterator pIterator = reader.readValues(s, Potato.class); + while(pIterator.hasNext()) { + Potato p = pIterator.next(); + sink(p); //$hasTaintFlow + sink(p.name); //$hasTaintFlow + sink(p.getName()); //$hasTaintFlow + } + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java new file mode 100644 index 00000000000..72e08df2719 --- /dev/null +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java @@ -0,0 +1,28 @@ +package com.fasterxml.jackson.databind; + +import java.io.Closeable; +import java.io.IOException; +import java.util.*; + +public class MappingIterator implements Iterator, Closeable { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public T next() { + return null; + } + + @Override + public void remove() { + + } + + @Override + public void close() throws IOException { + + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java index 1916730da36..875c07dace3 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -42,4 +42,41 @@ public class ObjectReader { public T readValue(Reader src, Class valueType) throws IOException { return null; } + + public MappingIterator readValues(String src) { + return null; + } + + public MappingIterator readValues(String src, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(byte[] content) throws IOException { + return null; + } + + public MappingIterator readValues(byte[] content, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(File src) throws IOException { + return null; + } + + public MappingIterator readValues(InputStream src) throws IOException { + return null; + } + + public MappingIterator readValues(InputStream src, Class valueType) throws IOException { + return null; + } + + public MappingIterator readValues(Reader src) throws IOException { + return null; + } + + public MappingIterator readValues(Reader src, Class valueType) throws IOException { + return null; + } + } \ No newline at end of file From d0b0b767a28d98ce40d3c2fddee477608757cdcd Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:23:33 -0400 Subject: [PATCH 319/550] Apply suggestions from code review Co-authored-by: Marcono1234 --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 1cfe34d7167..2ddb12e828d 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -75,7 +75,7 @@ private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { ExplicitlyReadJacksonSerializableType() { exists(MethodAccess ma | - // A call to a Jackson write method... + // A call to a Jackson read method... ma.getMethod() instanceof JacksonReadValueMethod and // ...where `this` is used in the final argument, indicating that this type will be deserialized. usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) @@ -126,8 +126,8 @@ private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa } /** A type used in a `JacksonDeserializableField` declaration. */ -private class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType { - FieldReferencedJacksonDeSerializableType() { +private class FieldReferencedJacksonDeserializableType extends JacksonDeserializableType { + FieldReferencedJacksonDeserializableType() { exists(JacksonDeserializableField f | usesType(f.getType(), this)) } } From b871f48c509b960c75847903300f7cbf7664fb6a Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 12:40:48 -0400 Subject: [PATCH 320/550] [Java] Add release note to Jackson change --- .../change-notes/2021-05-03-jackson-dataflow-deserialization.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-05-03-jackson-dataflow-deserialization.md diff --git a/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md new file mode 100644 index 00000000000..475f8dbee4f --- /dev/null +++ b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increase coverage of dataflow through Jackson JSON deserialized objects. From 83d527ed190331f4ecdb165539423922ee1f704a Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 3 May 2021 13:39:54 -0400 Subject: [PATCH 321/550] Apply suggestions from code review Co-authored-by: Marcono1234 --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 2ddb12e828d..0b75aff2b24 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -165,7 +165,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { * When an object is deserialized by the Jackson JSON framework using a tainted input source, * the fields that the framework deserialized are themselves tainted input data. */ -class JacksonDeseializedTaintStep extends AdditionalTaintStep { +class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() } From e97bad3b3395d3a6143b515bba1b12c344a881d2 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 4 May 2021 10:58:20 -0400 Subject: [PATCH 322/550] Support field access data flow for JacksonDeserializedTaintStep --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 0b75aff2b24..8cc70c5f36a 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -167,7 +167,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { */ class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr() + DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1 } } From bacc3ef5b3699f03bff628e8ca1196c0f7095342 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 10 May 2021 17:20:14 -0400 Subject: [PATCH 323/550] [Java] Jackson add support for 2 step deserialization taint flow --- .../semmle/code/java/dataflow/ExternalFlow.qll | 1 + .../frameworks/jackson/JacksonSerializability.qll | 11 +++++++++++ .../dataflow/taint-jackson/Test.java | 15 +++++++++++++++ .../com/fasterxml/jackson/databind/JsonNode.java | 4 +++- .../fasterxml/jackson/databind/ObjectMapper.java | 8 ++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index df529b49efb..1ec81d1e3ac 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -77,6 +77,7 @@ private module Frameworks { private import semmle.code.java.frameworks.ApacheHttp private import semmle.code.java.frameworks.apache.Lang private import semmle.code.java.frameworks.guava.Guava + private import semmle.code.java.frameworks.jackson.JacksonSerializability private import semmle.code.java.security.ResponseSplitting private import semmle.code.java.security.XSS private import semmle.code.java.security.LdapInjection diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 8cc70c5f36a..ad35a802d43 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -9,6 +9,7 @@ import semmle.code.java.Reflection import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DataFlow5 import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.ExternalFlow /** * A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation. @@ -275,3 +276,13 @@ class JacksonMixedInCallable extends Callable { ) } } + +private class JacksonModel extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint", + "com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint" + ] + } +} diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java index 26f9182339a..3be85336e26 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/Test.java +++ b/java/ql/test/library-tests/dataflow/taint-jackson/Test.java @@ -4,9 +4,12 @@ import java.io.OutputStream; import java.io.StringWriter; import java.io.Writer; import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ObjectReader; @@ -94,4 +97,16 @@ class Test { sink(p.getName()); //$hasTaintFlow } } + + public static void jacksonTwoStepDeserialization() throws java.io.IOException { + String s = taint(); + Map taintedParams = new HashMap<>(); + taintedParams.put("name", s); + ObjectMapper om = new ObjectMapper(); + JsonNode jn = om.valueToTree(taintedParams); + sink(jn); //$hasTaintFlow + Potato p = om.convertValue(jn, Potato.class); + sink(p); //$hasTaintFlow + sink(p.getName()); //$hasTaintFlow + } } diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java index a26eb2592c6..ad3da15a26c 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java @@ -1,6 +1,8 @@ package com.fasterxml.jackson.databind; -public class JsonNode { +import java.util.*; + +public abstract class JsonNode implements Iterable { public JsonNode() { } } \ No newline at end of file diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java index af6218e4146..71dc99a351d 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java @@ -30,4 +30,12 @@ public class ObjectMapper { public ObjectReader readerFor(Class type) { return null; } + + public T valueToTree(Object fromValue) throws IllegalArgumentException { + return null; + } + + public T convertValue(Object fromValue, Class toValueType) throws IllegalArgumentException { + return null; + } } From 5a68ac88efdf272c2f3e6dd9a9c5393b582bbcec Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 11 May 2021 10:48:22 -0400 Subject: [PATCH 324/550] Cleanup Jackson logic after code review --- .../jackson/JacksonSerializability.qll | 19 +++++++------------ .../dataflow/taint-jackson/dataFlow.ql | 2 +- .../fasterxml/jackson/databind/JsonNode.java | 2 +- .../jackson/databind/MappingIterator.java | 2 +- .../jackson/databind/ObjectReader.java | 2 +- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index ad35a802d43..09fd419642e 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -72,18 +72,6 @@ private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab } } -/** A type whose values are explicitly deserialized in a call to a Jackson method. */ -private class ExplicitlyReadJacksonSerializableType extends JacksonDeserializableType { - ExplicitlyReadJacksonSerializableType() { - exists(MethodAccess ma | - // A call to a Jackson read method... - ma.getMethod() instanceof JacksonReadValueMethod and - // ...where `this` is used in the final argument, indicating that this type will be deserialized. - usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) - ) - } -} - /** A type used in a `JacksonSerializableField` declaration. */ private class FieldReferencedJacksonSerializableType extends JacksonSerializableType { FieldReferencedJacksonSerializableType() { @@ -123,6 +111,13 @@ private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializa exists(TypeLiteralToJacksonDatabindFlowConfiguration conf | usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this) ) + or + exists(MethodAccess ma | + // A call to a Jackson read method... + ma.getMethod() instanceof JacksonReadValueMethod and + // ...where `this` is used in the final argument, indicating that this type will be deserialized. + usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this) + ) } } diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql index 3ee51339b7b..0836906530b 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql @@ -31,4 +31,4 @@ class HasFlowTest extends InlineExpectationsTest { value = "" ) } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java index ad3da15a26c..b04572cd4da 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/JsonNode.java @@ -5,4 +5,4 @@ import java.util.*; public abstract class JsonNode implements Iterable { public JsonNode() { } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java index 72e08df2719..ac427ef01c9 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/MappingIterator.java @@ -25,4 +25,4 @@ public class MappingIterator implements Iterator, Closeable { public void close() throws IOException { } -} \ No newline at end of file +} diff --git a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java index 875c07dace3..f067a3e95a4 100644 --- a/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java +++ b/java/ql/test/stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectReader.java @@ -79,4 +79,4 @@ public class ObjectReader { return null; } -} \ No newline at end of file +} From e7cd6c997208b742129bd72c0715a27790130211 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 11 May 2021 16:56:12 +0000 Subject: [PATCH 325/550] Optimize the query --- .../Security/CWE/CWE-094/JythonInjection.java | 6 ++-- .../Security/CWE/CWE-094/JythonInjection.ql | 35 +++++++++---------- .../security/CWE-094/JythonInjection.java | 4 +-- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java index fca518443d1..5c1796e1f60 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -1,3 +1,5 @@ +import org.python.util.PythonInterpreter; + public class JythonInjection extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); @@ -10,7 +12,7 @@ public class JythonInjection extends HttpServlet { interpreter.setOut(out); interpreter.setErr(out); - // BAD: allow arbitrary Jython expression to execute + // BAD: allow execution of arbitrary Python code interpreter.exec(code); out.flush(); @@ -32,7 +34,7 @@ public class JythonInjection extends HttpServlet { try { interpreter = new PythonInterpreter(); - // BAD: allow arbitrary Jython expression to evaluate + // BAD: allow execution of arbitrary Python code PyObject py = interpreter.eval(code); response.getWriter().print(py.toString()); diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 088c33e00fd..9991c0901dc 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -11,6 +11,7 @@ import java import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.spring.SpringController import DataFlow::PathGraph /** The class `org.python.util.PythonInterpreter`. */ @@ -22,12 +23,7 @@ class PythonInterpreter extends RefType { class InterpretExprMethod extends Method { InterpretExprMethod() { this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and - ( - getName().matches("exec%") or - hasName("eval") or - hasName("compile") or - getName().matches("run%") - ) + getName().matches(["exec%", "run%", "eval", "compile"]) } } @@ -48,14 +44,14 @@ predicate runCode(MethodAccess ma, Expr sink) { class LoadClassMethod extends Method { LoadClassMethod() { this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and - ( - hasName("makeClass") or - hasName("makeCode") - ) + hasName(["makeClass", "makeCode"]) } } -/** Holds if a Java class file is loaded. */ +/** + * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array + * representing the class to be loaded. + */ predicate loadClass(MethodAccess ma, Expr sink) { exists(Method m, int i | m = ma.getMethod() | m instanceof LoadClassMethod and @@ -69,7 +65,7 @@ class Py extends RefType { Py() { this.hasQualifiedName("org.python.core", "Py") } } -/** A method that compiles code with `Py`. */ +/** A method declared on class `Py` or one of its descendants that compiles Python code. */ class PyCompileMethod extends Method { PyCompileMethod() { this.getDeclaringType().getAnAncestor*() instanceof Py and @@ -85,7 +81,7 @@ predicate compile(MethodAccess ma, Expr sink) { ) } -/** Sink of an expression loaded by Jython. */ +/** An expression loaded by Jython. */ class CodeInjectionSink extends DataFlow::ExprNode { CodeInjectionSink() { runCode(_, this.getExpr()) or @@ -103,17 +99,18 @@ class CodeInjectionSink extends DataFlow::ExprNode { class CodeInjectionConfiguration extends TaintTracking::Configuration { CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | ma.getQualifier() = node1.asExpr() and ma = node2.asExpr()) + exists(MethodAccess ma | + ma.getMethod().getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and + not ma.getMethod().getDeclaringType() instanceof TypeObject and + ma.getQualifier() = node1.asExpr() and + ma = node2.asExpr() + ) } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java index 682e8af5113..f9b29fec6cc 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java +++ b/java/ql/test/experimental/query-tests/security/CWE-094/JythonInjection.java @@ -22,7 +22,7 @@ public class JythonInjection extends HttpServlet { super(); } - // BAD: allow arbitrary Jython expression to execute + // BAD: allow execution of arbitrary Python code protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); String code = request.getParameter("code"); @@ -47,7 +47,7 @@ public class JythonInjection extends HttpServlet { } } - // BAD: allow arbitrary Jython expression to evaluate + // BAD: allow execution of arbitrary Python code protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); String code = request.getParameter("code"); From 8969da7775e88116959fee9ff79c5d3396233d79 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Tue, 11 May 2021 19:21:40 +0200 Subject: [PATCH 326/550] Java: Improve not closing resource query; add tests --- .../Likely Bugs/Resource Leaks/CloseReader.ql | 4 +- .../Likely Bugs/Resource Leaks/CloseWriter.ql | 6 +- .../CloseReader/CloseReader.expected | 6 +- .../CloseReader/CloseReader.java | 89 ++++++++---- .../CloseReader/CloseReader.qlref | 2 +- .../CloseWriter/CloseWriter.expected | 3 + .../CloseWriter/CloseWriter.java | 131 ++++++++++++++++++ .../CloseWriter/CloseWriter.qlref | 1 + 8 files changed, 205 insertions(+), 37 deletions(-) create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java create mode 100644 java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql index 5993bd1de2b..73a9c81f343 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql @@ -17,14 +17,14 @@ import CloseType predicate readerType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName(["Reader", "InputStream"]) or + sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or sup.hasQualifiedName("java.util.zip", "ZipFile") ) } predicate safeReaderType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName(["CharArrayReader", "StringReader", "ByteArrayInputStream"]) + sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"]) ) } diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql index 824951f86f5..a0714fe3a2f 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql @@ -16,12 +16,14 @@ import CloseType predicate writerType(RefType t) { - exists(RefType sup | sup = t.getASupertype*() | sup.hasName(["Writer", "OutputStream"])) + exists(RefType sup | sup = t.getASupertype*() | + sup.hasQualifiedName("java.io", ["Writer", "OutputStream"]) + ) } predicate safeWriterType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName(["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"]) + sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"]) ) } diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected index 12d09e0d65a..e76a4c6d1e4 100644 --- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected +++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.expected @@ -1,2 +1,4 @@ -| CloseReader.java:11:42:11:71 | new FileReader(...) | This FileReader is not always closed on method exit. | -| CloseReader.java:44:6:44:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. | +| CloseReader.java:18:42:18:71 | new FileReader(...) | This FileReader is not always closed on method exit. | +| CloseReader.java:23:20:23:50 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. | +| CloseReader.java:33:6:33:40 | new FileInputStream(...) | This FileInputStream is not always closed on method exit. | +| CloseReader.java:43:21:43:43 | new ZipFile(...) | This ZipFile is not always closed on method exit. | diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java index 7d78f8dbfef..b77afc49105 100644 --- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java +++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.java @@ -1,41 +1,30 @@ import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.io.Closeable; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; -import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.zip.ZipFile; class CloseReader { - public static void test1() throws IOException { + void test1() throws IOException { BufferedReader br = new BufferedReader(new FileReader("C:\\test.txt")); System.out.println(br.readLine()); } - public static void test2() throws FileNotFoundException, IOException { - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader("C:\\test.txt")); - System.out.println(br.readLine()); - } - finally { - if(br != null) - br.close(); // 'br' is closed - } + void test2() throws IOException { + InputStream in = new FileInputStream("file.bin"); + in.read(); } - public static void test3() throws IOException { - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader("C:\\test.txt")); - System.out.println(br.readLine()); - } - finally { - cleanup(br); // 'br' is closed within a helper method - } - } - - public static void test4() throws IOException { + void test3() throws IOException { InputStreamReader reader = null; try { // InputStreamReader may throw an exception, in which case the ... @@ -50,7 +39,35 @@ class CloseReader { } } - public static void test5() throws IOException { + void test4() throws IOException { + ZipFile zipFile = new ZipFile("file.zip"); + System.out.println(zipFile.getComment()); + } + + void testCorrect1() throws IOException { + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader("C:\\test.txt")); + System.out.println(br.readLine()); + } + finally { + if(br != null) + br.close(); // 'br' is closed + } + } + + void testCorrect2() throws IOException { + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader("C:\\test.txt")); + System.out.println(br.readLine()); + } + finally { + cleanup(br); // 'br' is closed within a helper method + } + } + + void testCorrect3() throws IOException { FileInputStream fis = null; InputStreamReader reader = null; try { @@ -66,7 +83,7 @@ class CloseReader { } } - public static void test6() throws IOException { + void testCorrect4() throws IOException { BufferedReader br = null; try { br = new BufferedReader(new FileReader("C:\\test.txt")); @@ -77,15 +94,15 @@ class CloseReader { } } - public static void cleanup(java.io.Closeable... closeables) throws IOException { - for (java.io.Closeable c : closeables) { + void cleanup(Closeable... closeables) throws IOException { + for (Closeable c : closeables) { if (c != null) { c.close(); } } } - public static class LogFile { + static class LogFile { private BufferedReader fileRd; LogFile(String path) { FileReader fr = null; @@ -100,9 +117,21 @@ class CloseReader { private void init(InputStreamReader reader) { fileRd = new BufferedReader(reader); } - public void readStuff() throws java.io.IOException { + public void readStuff() throws IOException { System.out.println(fileRd.readLine()); fileRd.close(); } } + + // Classes which should be ignored + void testIgnore() throws IOException { + Reader r1 = new CharArrayReader(new char[] {'a'}); + r1.read(); + + Reader r2 = new StringReader("a"); + r2.read(); + + InputStream i1 = new ByteArrayInputStream(new byte[] {1}); + i1.read(); + } } diff --git a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref index 3c76645e809..1c808bb9f46 100644 --- a/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref +++ b/java/ql/test/query-tests/CloseResource/CloseReader/CloseReader.qlref @@ -1 +1 @@ -Likely Bugs/Resource Leaks/CloseReader.ql \ No newline at end of file +Likely Bugs/Resource Leaks/CloseReader.ql diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected new file mode 100644 index 00000000000..efbfe15f2c9 --- /dev/null +++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.expected @@ -0,0 +1,3 @@ +| CloseWriter.java:17:42:17:71 | new FileWriter(...) | This FileWriter is not always closed on method exit. | +| CloseWriter.java:22:22:22:53 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. | +| CloseWriter.java:32:6:32:41 | new FileOutputStream(...) | This FileOutputStream is not always closed on method exit. | diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java new file mode 100644 index 00000000000..3733237b8de --- /dev/null +++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.java @@ -0,0 +1,131 @@ +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayWriter; +import java.io.Closeable; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.util.zip.ZipFile; + +class CloseWriter { + + void test1() throws IOException { + BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\test.txt")); + bw.write("test"); + } + + void test2() throws IOException { + OutputStream out = new FileOutputStream("test.bin"); + out.write(1); + } + + void test3() throws IOException { + OutputStreamWriter writer = null; + try { + // OutputStreamWriter may throw an exception, in which case the ... + writer = new OutputStreamWriter( + // ... FileOutputStream is not closed by the finally block + new FileOutputStream("C:\\test.txt"), "UTF-8"); + writer.write("test"); + } + finally { + if (writer != null) + writer.close(); + } + } + + void testCorrect1() throws IOException { + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter("C:\\test.txt")); + bw.write("test"); + } + finally { + if(bw != null) + bw.close(); // 'bw' is closed + } + } + + void testCorrect2() throws IOException { + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter("C:\\test.txt")); + bw.write("test"); + } + finally { + cleanup(bw); // 'bw' is closed within a helper method + } + } + + void testCorrect3() throws IOException { + FileOutputStream fos = null; + OutputStreamWriter writer = null; + try { + fos = new FileOutputStream("C:\\test.txt"); + writer = new OutputStreamWriter(fos); + writer.write("test"); + } + finally { + if (fos != null) + fos.close(); // 'fos' is closed + if (writer != null) + writer.close(); // 'writer' is closed + } + } + + void testCorrect4() throws IOException { + BufferedWriter bw = null; + try { + bw = new BufferedWriter(new FileWriter("C:\\test.txt")); + bw.write("test"); + } + finally { + cleanup(null, bw); // 'bw' is closed within a varargs helper method, invoked with multiple args + } + } + + void cleanup(Closeable... closeables) throws IOException { + for (Closeable c : closeables) { + if (c != null) { + c.close(); + } + } + } + + static class LogFile { + private BufferedWriter fileWr; + LogFile(String path) { + FileWriter fw = null; + try { + fw = new FileWriter(path); + } catch (IOException e) { + System.out.println("Error: File not readable: " + path); + System.exit(1); + } + init(fw); + } + private void init(OutputStreamWriter writer) { + fileWr = new BufferedWriter(writer); + } + public void writeStuff() throws IOException { + fileWr.write("test"); + fileWr.close(); + } + } + + // Classes which should be ignored + void testIgnore() throws IOException { + Writer w1 = new CharArrayWriter(); + w1.write("test"); + + Writer w2 = new StringWriter(); + w2.write("test"); + + OutputStream o1 = new ByteArrayOutputStream(); + o1.write(1); + } +} diff --git a/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref new file mode 100644 index 00000000000..88008367363 --- /dev/null +++ b/java/ql/test/query-tests/CloseResource/CloseWriter/CloseWriter.qlref @@ -0,0 +1 @@ +Likely Bugs/Resource Leaks/CloseWriter.ql From 948f1d8e344935c7b550450813e61f4df415ba6e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 11 May 2021 19:43:21 +0200 Subject: [PATCH 327/550] C++: Add testcase with INTMAX_MIN. --- .../Security/CWE/CWE-190/semmle/tainted/test5.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index 92eb71ad541..2ee675be6b5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -29,4 +29,14 @@ void useTaintedIntWithGuard() { if(imaxabs(tainted) <= 100) { int product = tainted * tainted; // GOOD: can't underflow/overflow } +} + +#define INTMAX_MIN (-0x7fffffffffffffff - 1) + +void useTaintedIntWithGuardIntMaxMin() { + intmax_t tainted = getTaintedInt(); + + if(imaxabs(tainted) <= INTMAX_MIN) { + int product = tainted * tainted; // BAD: imaxabs(INTMAX_MIN) == INTMAX_MIN [NOT DETECTED] + } } \ No newline at end of file From 8e371fd05a842648d78762238c4f2b8cd5c531f4 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 11 May 2021 21:54:05 +0200 Subject: [PATCH 328/550] Adjust expected IR test file --- csharp/ql/test/experimental/ir/ir/raw_ir.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index 646cd0f26f9..c598a8778d9 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -613,13 +613,13 @@ foreach.cs: # 5| r5_28(glval) = PointerAdd[4] : r5_1, r5_27 # 5| r5_29(Int32) = Constant[7] : # 5| mu5_30(Int32) = Store[?] : &:r5_28, r5_29 -# 7| r7_1(glval) = VariableAddress[#temp7:9] : +# 7| r7_1(glval) = VariableAddress[#temp7:9] : # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load[a_array] : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : # 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? -# 7| mu7_7(IEnumerator) = Store[#temp7:9] : &:r7_1, r7_5 +# 7| mu7_7(Boolean) = Store[#temp7:9] : &:r7_1, r7_5 #-----| Goto -> Block 1 # 7| Block 1 From 961467e06e4142ca71d144ee5c1bb7ca993b706b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 12 May 2021 10:15:04 +0200 Subject: [PATCH 329/550] C#: Always pass `/p:UseSharedCompilation=false` to `dotnet build` in auto builder --- .../BuildScripts.cs | 44 ++++----------- .../Semmle.Autobuild.CSharp/DotNetRule.cs | 56 ++++--------------- 2 files changed, 21 insertions(+), 79 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 99ad4c8f963..197edc2c162 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -415,7 +415,7 @@ namespace Semmle.Autobuild.CSharp.Tests actions.RunProcess["cmd.exe /C dotnet --info"] = 0; 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:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -439,9 +439,6 @@ namespace Semmle.Autobuild.CSharp.Tests [Fact] public void TestLinuxCSharpAutoBuilder() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -463,7 +460,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, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -603,8 +600,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestLinuxBuildCommand() { - 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.FileExists["csharp.log"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -615,7 +610,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, 2); + TestAutobuilderScript(autobuilder, 0, 1); } [Fact] @@ -626,14 +621,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - 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.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 2); } [Fact] @@ -645,14 +638,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = false; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -664,14 +655,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -872,9 +861,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestSkipNugetDotnet() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -896,7 +882,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, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -907,13 +893,10 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; 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:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists["test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -933,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, 9); + TestAutobuilderScript(autobuilder, 0, 8); } [Fact] @@ -945,11 +928,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] -Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; @@ -973,7 +951,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, 9); + TestAutobuilderScript(autobuilder, 0, 8); } private void TestDotnetVersionWindows(Action action, int commandsRun) @@ -984,7 +962,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; 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:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index a456c9407db..955775d0f66 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -36,7 +36,7 @@ namespace Semmle.Autobuild.CSharp builder.Log(Severity.Info, "Attempting to build using .NET Core"); } - return WithDotNet(builder, (dotNetPath, environment, compatibleClr) => + return WithDotNet(builder, (dotNetPath, environment) => { var ret = GetInfoCommand(builder.Actions, dotNetPath, environment); foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) @@ -49,7 +49,7 @@ namespace Semmle.Autobuild.CSharp restoreCommand.QuoteArgument(projectOrSolution.FullPath); var restore = restoreCommand.Script; - var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath); + var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath); ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build; } @@ -57,7 +57,7 @@ namespace Semmle.Autobuild.CSharp }); } - private static BuildScript WithDotNet(Autobuilder builder, Func?, bool, BuildScript> f) + private static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) { var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet"); var installScript = DownloadDotNet(builder, installDir); @@ -81,35 +81,10 @@ namespace Semmle.Autobuild.CSharp env = null; } - // The CLR tracer is always compatible on Windows - if (builder.Actions.IsWindows()) - return f(installDir, env, true); - - // The CLR tracer is only compatible on .NET Core >= 3 on Linux and macOS (see - // https://github.com/dotnet/coreclr/issues/19622) - return BuildScript.Bind(GetInstalledRuntimesScript(builder.Actions, installDir, env), (runtimes, runtimesRet) => - { - var compatibleClr = false; - if (runtimesRet == 0) - { - var minimumVersion = new Version(3, 0); - var regex = new Regex(@"Microsoft\.NETCore\.App (\d\.\d\.\d)"); - compatibleClr = runtimes - .Select(runtime => regex.Match(runtime)) - .Where(m => m.Success) - .Select(m => m.Groups[1].Value) - .Any(m => Version.TryParse(m, out var v) && v >= minimumVersion); - } - - if (!compatibleClr) - { - if (env is null) - env = new Dictionary(); - env.Add("UseSharedCompilation", "false"); - } - - return f(installDir, env, compatibleClr); - }); + if (env is null) + env = new Dictionary(); + env.Add("UseSharedCompilation", "false"); + return f(installDir, env); }); } @@ -122,7 +97,7 @@ namespace Semmle.Autobuild.CSharp /// are needed). /// public static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) - => WithDotNet(builder, (_1, env, _2) => f(env)); + => WithDotNet(builder, (_1, env) => f(env)); /// /// Returns a script for downloading relevant versions of the @@ -259,14 +234,6 @@ namespace Semmle.Autobuild.CSharp return restore; } - private static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary? environment) - { - var listSdks = new CommandBuilder(actions, environment: environment, silent: true). - RunCommand(DotNetCommand(actions, dotNetPath)). - Argument("--list-runtimes"); - return listSdks.Script; - } - /// /// Gets the `dotnet build` script. /// @@ -276,17 +243,14 @@ namespace Semmle.Autobuild.CSharp /// hence the need for CLR tracing), by adding a /// `/p:UseSharedCompilation=false` argument. /// - private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, bool compatibleClr, string projOrSln) + private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { var build = new CommandBuilder(builder.Actions, null, environment); var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)). Argument("build"). Argument("--no-incremental"); - return compatibleClr ? - script.Argument(builder.Options.DotNetArguments). - QuoteArgument(projOrSln). - Script : + return script.Argument("/p:UseSharedCompilation=false"). Argument(builder.Options.DotNetArguments). QuoteArgument(projOrSln). From 07a70af3448c76e1ae31a886ac8ec13eafa26e6a Mon Sep 17 00:00:00 2001 From: Taus Date: Tue, 11 May 2021 19:41:39 +0000 Subject: [PATCH 330/550] Python: Limit set of globals that may be built-ins I am very tempted to leave out the constants, or at the very least `False`, `True`, and `None`, as these have _many_ occurrences in the average codebase, and are not terribly useful at the API-graph level. If we really do want to capture "nodes that refer to such and such constant", then I think a better solution would be to create classes extending `DataFlow::Node` to facilitate this. --- python/ql/src/semmle/python/ApiGraphs.qll | 70 ++++++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/python/ql/src/semmle/python/ApiGraphs.qll b/python/ql/src/semmle/python/ApiGraphs.qll index a530e44e85d..86ad81e7454 100644 --- a/python/ql/src/semmle/python/ApiGraphs.qll +++ b/python/ql/src/semmle/python/ApiGraphs.qll @@ -351,20 +351,74 @@ module API { private import semmle.python.types.Builtins as Builtins + /** Returns the names of known built-ins. */ + private string builtin_name() { + // Built-in functions shared between Python 2.7.6 and 3.9.5 + result in [ + "abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", "classmethod", + "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "filter", + "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", + "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", + "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", + "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", + "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__" + ] + or + // Built-in constants shared between Python 2.7.6 and 3.9.5 + result in ["False", "True", "None", "NotImplemented", "Ellipsis", "__debug__"] + or + // Python 3.9.5 only + major_version() = 3 and + result in ["ascii", "breakpoint", "bytes", "exec"] + or + // Python 2.7.6 only + major_version() = 2 and + result in [ + "basestring", "cmp", "execfile", "file", "long", "raw_input", "reduce", "reload", + "unichr", "unicode", "xrange" + ] + } + /** * Gets a data flow node that is likely to refer to a built-in with the name `name`. * - * Currently this is an over-approximation, and does not account for things like overwriting a + * Currently this is an over-approximation, and may not account for things like overwriting a * built-in with a different value. */ private DataFlow::Node likely_builtin(string name) { - result.asCfgNode() = - any(NameNode n | - n.isGlobal() and - n.isLoad() and - name = n.getId() and - name in [any(Builtins::Builtin b).getName(), "None", "True", "False"] - ) + exists(Module m | + result.asCfgNode() = + any(NameNode n | + possible_builtin_accessed_in_module(n, name, m) and + not possible_builtin_defined_in_module(name, m) + ) + ) + } + + /** + * Holds if a global variable called `name` (which is also the name of a built-in) is assigned + * a value in the module `m`. + */ + private predicate possible_builtin_defined_in_module(string name, Module m) { + exists(NameNode n | + n.isGlobal() and + n.isStore() and + name = n.getId() and + name = builtin_name() and + m = n.getEnclosingModule() + ) + } + + /** + * Holds if `n` is an access of a global variable called `name` (which is also the name of a + * built-in) inside the module `m`. + */ + private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) { + n.isGlobal() and + n.isLoad() and + name = n.getId() and + name = builtin_name() and + m = n.getEnclosingModule() } /** From bf4d88175c9727414780b04802fa0e1ed8e92e71 Mon Sep 17 00:00:00 2001 From: Sebastian Bauersfeld Date: Wed, 12 May 2021 16:40:00 +0700 Subject: [PATCH 331/550] Consider boxed booleans to avoid false positives for XXE.ql --- java/ql/src/semmle/code/java/security/XmlParsers.qll | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/XmlParsers.qll b/java/ql/src/semmle/code/java/security/XmlParsers.qll index 685c5754fc9..5e2eb1caf8a 100644 --- a/java/ql/src/semmle/code/java/security/XmlParsers.qll +++ b/java/ql/src/semmle/code/java/security/XmlParsers.qll @@ -36,7 +36,10 @@ abstract class ParserConfig extends MethodAccess { */ predicate disables(Expr e) { this.getArgument(0) = e and - this.getArgument(1).(BooleanLiteral).getBooleanValue() = false + ( + this.getArgument(1).(BooleanLiteral).getBooleanValue() = false or + this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "FALSE") + ) } /** @@ -44,7 +47,10 @@ abstract class ParserConfig extends MethodAccess { */ predicate enables(Expr e) { this.getArgument(0) = e and - this.getArgument(1).(BooleanLiteral).getBooleanValue() = true + ( + this.getArgument(1).(BooleanLiteral).getBooleanValue() = true or + this.getArgument(1).(FieldAccess).getField().hasQualifiedName("java.lang", "Boolean", "TRUE") + ) } } From 5c7e73d4854ac817a10d32fb44e6b7272b5f11ef Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 09:51:55 +0000 Subject: [PATCH 332/550] Python: Add exception types --- python/ql/src/semmle/python/ApiGraphs.qll | 31 ++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/python/ql/src/semmle/python/ApiGraphs.qll b/python/ql/src/semmle/python/ApiGraphs.qll index 86ad81e7454..177d0f3998e 100644 --- a/python/ql/src/semmle/python/ApiGraphs.qll +++ b/python/ql/src/semmle/python/ApiGraphs.qll @@ -353,7 +353,7 @@ module API { /** Returns the names of known built-ins. */ private string builtin_name() { - // Built-in functions shared between Python 2.7.6 and 3.9.5 + // Built-in functions and exceptions shared between Python 2 and 3 result in [ "abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "filter", @@ -361,17 +361,36 @@ module API { "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", - "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__" + "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__", + // Exceptions + "ArithmeticError", "AssertionError", "AttributeError", "BaseException", "BufferError", + "BytesWarning", "DeprecationWarning", "EOFError", "EnvironmentError", "Exception", + "FloatingPointError", "FutureWarning", "GeneratorExit", "IOError", "ImportError", + "ImportWarning", "IndentationError", "IndexError", "KeyError", "KeyboardInterrupt", + "LookupError", "MemoryError", "NameError", "NotImplemented", "NotImplementedError", + "OSError", "OverflowError", "PendingDeprecationWarning", "ReferenceError", "RuntimeError", + "RuntimeWarning", "StandardError", "StopIteration", "SyntaxError", "SyntaxWarning", + "SystemError", "SystemExit", "TabError", "TypeError", "UnboundLocalError", + "UnicodeDecodeError", "UnicodeEncodeError", "UnicodeError", "UnicodeTranslateError", + "UnicodeWarning", "UserWarning", "ValueError", "Warning", "ZeroDivisionError" ] or - // Built-in constants shared between Python 2.7.6 and 3.9.5 + // Built-in constants shared between Python 2 and 3 result in ["False", "True", "None", "NotImplemented", "Ellipsis", "__debug__"] or - // Python 3.9.5 only + // Python 3 only major_version() = 3 and - result in ["ascii", "breakpoint", "bytes", "exec"] + result in [ + "ascii", "breakpoint", "bytes", "exec", + // Exceptions + "BlockingIOError", "BrokenPipeError", "ChildProcessError", "ConnectionAbortedError", + "ConnectionError", "ConnectionRefusedError", "ConnectionResetError", "FileExistsError", + "FileNotFoundError", "InterruptedError", "IsADirectoryError", "ModuleNotFoundError", + "NotADirectoryError", "PermissionError", "ProcessLookupError", "RecursionError", + "ResourceWarning", "StopAsyncIteration", "TimeoutError" + ] or - // Python 2.7.6 only + // Python 2 only major_version() = 2 and result in [ "basestring", "cmp", "execfile", "file", "long", "raw_input", "reduce", "reload", From b05512a9585def1ecb9fd2c35cfebbcc70ab6ef8 Mon Sep 17 00:00:00 2001 From: Sebastian Bauersfeld Date: Wed, 12 May 2021 16:58:24 +0700 Subject: [PATCH 333/550] Add change notes. --- java/change-notes/2021-05-12-xxe-fp-fix.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-05-12-xxe-fp-fix.md diff --git a/java/change-notes/2021-05-12-xxe-fp-fix.md b/java/change-notes/2021-05-12-xxe-fp-fix.md new file mode 100644 index 00000000000..dd42bc71256 --- /dev/null +++ b/java/change-notes/2021-05-12-xxe-fp-fix.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Resolving XML external entity in user-controlled data" (`java/xxe`) has been improved to report fewer false positives when a Builder / Factory (e.g. an `XMLInputFactory`) is configured safely by using a boxed boolean as second argument to one or more of its configuration methods. From 3d30efed112473c87e213f0e7feed3b0196fc8de Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 11:07:16 +0000 Subject: [PATCH 334/550] Python: Add `exec` as a shared built-in This is _slightly_ wrong, since `exec` isn't a built-in function in Python 2. It should be harmless, however, since `exec` is a keyword, and so cannot be redefined anyway. --- python/ql/src/semmle/python/ApiGraphs.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/ApiGraphs.qll b/python/ql/src/semmle/python/ApiGraphs.qll index 177d0f3998e..9ad125f86bc 100644 --- a/python/ql/src/semmle/python/ApiGraphs.qll +++ b/python/ql/src/semmle/python/ApiGraphs.qll @@ -372,7 +372,9 @@ module API { "RuntimeWarning", "StandardError", "StopIteration", "SyntaxError", "SyntaxWarning", "SystemError", "SystemExit", "TabError", "TypeError", "UnboundLocalError", "UnicodeDecodeError", "UnicodeEncodeError", "UnicodeError", "UnicodeTranslateError", - "UnicodeWarning", "UserWarning", "ValueError", "Warning", "ZeroDivisionError" + "UnicodeWarning", "UserWarning", "ValueError", "Warning", "ZeroDivisionError", + // Added for compatibility + "exec" ] or // Built-in constants shared between Python 2 and 3 From 48b50f93c27c60ad41ebd112f6b699ac987503fa Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Wed, 12 May 2021 08:58:01 -0400 Subject: [PATCH 335/550] Update java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll Co-authored-by: Tony Torralba --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 09fd419642e..50266c377f8 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -53,7 +53,10 @@ private class JacksonWriteValueMethod extends Method, TaintPreservingCallable { private class JacksonReadValueMethod extends Method, TaintPreservingCallable { JacksonReadValueMethod() { - getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and + ( + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or + getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") + ) and hasName(["readValue", "readValues"]) } From e94dab70b50ce64f692fb6e8db93149ce063f9d5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 12 May 2021 15:44:09 +0200 Subject: [PATCH 336/550] C++: Add sanitizers to cpp/uncontrolled-arithmetic. --- .../CWE/CWE-190/ArithmeticUncontrolled.ql | 111 +++++++++++++----- .../ArithmeticUncontrolled.expected | 46 -------- .../CWE/CWE-190/semmle/uncontrolled/test.c | 6 +- 3 files changed, 84 insertions(+), 79 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql index a4b0f131d14..6aad6cca7ce 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -15,34 +15,99 @@ import cpp import semmle.code.cpp.security.Overflow import semmle.code.cpp.security.Security import semmle.code.cpp.security.TaintTracking +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import TaintedWithPath -predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" } - -predicate isRandCallOrParent(Expr e) { - isRandCall(e) or - isRandCallOrParent(e.getAChild()) +predicate isUnboundedRandCall(FunctionCall fc) { + fc.getTarget().getName() = "rand" and not bounded(fc) } -predicate isRandValue(Expr e) { - isRandCall(e) +/** + * An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or + * a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division. + */ +pragma[inline] +predicate boundedDiv(Expr e, Expr left) { e = left } + +/** + * An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or + * an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded + * when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer + * allowed by the result type of `rem`. + */ +pragma[inline] +predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) { + e = left and + upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted()) +} + +/** + * An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr` + * or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper + * bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`. + */ +pragma[inline] +predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) { + operand1 != operand2 and + e = operand1 and + upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted()) +} + +/** + * Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range + * of possible values. + */ +predicate bounded(Expr e) { + // For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the + // maximum possible value of the result type of the operation. + // For example, the function call `rand()` is considered bounded in the following program: + // ``` + // int i = rand() % (UINT8_MAX + 1); + // ``` + // but not in: + // ``` + // unsigned char uc = rand() % (UINT8_MAX + 1); + // ``` + exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand())) + or + exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue())) + or + exists(BitwiseAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + exists(AssignAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + // Optimitically assume that a division always yields a much smaller value. + boundedDiv(e, any(DivExpr div).getLeftOperand()) + or + boundedDiv(e, any(AssignDivExpr div).getLValue()) +} + +predicate isUnboundedRandCallOrParent(Expr e) { + isUnboundedRandCall(e) + or + isUnboundedRandCallOrParent(e.getAChild()) +} + +predicate isUnboundedRandValue(Expr e) { + isUnboundedRandCall(e) or exists(MacroInvocation mi | e = mi.getExpr() and - isRandCallOrParent(e) + isUnboundedRandCallOrParent(e) ) } class SecurityOptionsArith extends SecurityOptions { override predicate isUserInput(Expr expr, string cause) { - isRandValue(expr) and - cause = "rand" and - not expr.getParent*() instanceof DivExpr + isUnboundedRandValue(expr) and + cause = "rand" } } -predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) } - predicate missingGuard(VariableAccess va, string effect) { exists(Operation op | op.getAnOperand() = va | missingGuardAgainstUnderflow(op, va) and effect = "underflow" @@ -52,29 +117,15 @@ predicate missingGuard(VariableAccess va, string effect) { } class Configuration extends TaintTrackingConfiguration { - override predicate isSink(Element e) { - isDiv(e) - or - missingGuard(e, _) - } -} + override predicate isSink(Element e) { missingGuard(e, _) } -/** - * A value that undergoes division is likely to be bounded within a safe - * range. - */ -predicate guardedByAssignDiv(Expr origin) { - exists(VariableAccess va | - taintedWithPath(origin, va, _, _) and - isDiv(va) - ) + override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) } } from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode where taintedWithPath(origin, va, sourceNode, sinkNode) and - missingGuard(va, effect) and - not guardedByAssignDiv(origin) + missingGuard(va, effect) select va, sourceNode, sinkNode, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin, "Uncontrolled value" diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected index ca8dd38fc3b..097efb73b9f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected @@ -7,30 +7,10 @@ edges | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | @@ -67,34 +47,11 @@ nodes | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:77:9:77:9 | r | semmle.label | r | @@ -133,10 +90,7 @@ nodes #select | test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value | | test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value | -| test.c:40:5:40:5 | r | test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value | | test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value | -| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value | -| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value | | test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value | | test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value | | test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c index 2b67b499a3c..61f39a8e851 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c @@ -37,7 +37,7 @@ void randomTester() { { int r = RANDN(100); - r += 100; // GOOD: The return from RANDN is bounded [FALSE POSITIVE] + r += 100; // GOOD: The return from RANDN is bounded } { @@ -53,7 +53,7 @@ void randomTester() { { int r = rand(); r = r / 10; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { @@ -64,7 +64,7 @@ void randomTester() { { int r = rand() & 0xFF; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { From e0f78dde56f7f31719c91cd0367922046edaa2ab Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 12 May 2021 16:23:37 +0200 Subject: [PATCH 337/550] make the axios error catch match the non-error case --- .../ql/src/semmle/javascript/frameworks/ClientRequests.qll | 3 +-- .../frameworks/ClientRequests/ClientRequests.expected | 4 +++- .../ql/test/library-tests/frameworks/ClientRequests/tst.js | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index 477f9354d03..bce5d6a36fc 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -271,8 +271,7 @@ module ClientRequest { or responseType = getResponseType() and promise = false and - result = - getReturn().getPromisedError().getMember("response").getMember("data").getAnImmediateUse() + result = getReturn().getPromisedError().getMember("response").getAnImmediateUse() } } diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index c6542eb009d..27a9fa10f72 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -298,4 +298,6 @@ test_getAResponseDataNode | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false | | tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:5:299:6 | axios({ ... \\n }) | json | true | -| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:42 | err.response.data | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:302:28:302:39 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:37 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:304:27:304:38 | err.response | json | false | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index f284ffaa407..40dcfc481f4 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -301,6 +301,7 @@ function moreAxios() { (err) => { const status = err.response.status; const data = err.response.data; + const agent = err.response.headers.useragent; } ); } \ No newline at end of file From 7d26aca793353c8564d5cac8715dbcecd87aa917 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 12 May 2021 16:34:23 +0200 Subject: [PATCH 338/550] C++: Add change-note. --- cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md diff --git a/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md b/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md new file mode 100644 index 00000000000..56fbc9a44ce --- /dev/null +++ b/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives. From ff2b6b9737b608c1d99752ba4a2df521863eb5ef Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 18:07:18 +0000 Subject: [PATCH 339/550] Python: Correctly locate stores to built-ins --- python/ql/src/semmle/python/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/ApiGraphs.qll b/python/ql/src/semmle/python/ApiGraphs.qll index 9ad125f86bc..080c967ed2d 100644 --- a/python/ql/src/semmle/python/ApiGraphs.qll +++ b/python/ql/src/semmle/python/ApiGraphs.qll @@ -422,7 +422,7 @@ module API { */ private predicate possible_builtin_defined_in_module(string name, Module m) { exists(NameNode n | - n.isGlobal() and + not exists(LocalVariable v | n.defines(v)) and n.isStore() and name = n.getId() and name = builtin_name() and From 109fa4d38e598ef7c8558afcbd0cd58655362067 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 12 May 2021 13:54:07 +0100 Subject: [PATCH 340/550] C++: Add test cases for BrokenCryptoAlgorithm.ql. --- .../CWE-327/BrokenCryptoAlgorithm.expected | 29 ++ .../CWE/CWE-327/BrokenCryptoAlgorithm.qlref | 1 + .../query-tests/Security/CWE/CWE-327/test.cpp | 109 ++++++++ .../Security/CWE/CWE-327/test2.cpp | 254 ++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected new file mode 100644 index 00000000000..47f6e1daa7e --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -0,0 +1,29 @@ +| test2.cpp:25:2:25:9 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:33:7:33:14 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:47:7:47:14 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:62:33:62:40 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:172:28:172:35 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:192:26:192:33 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:41:2:41:32 | ENCRYPT_WITH_3DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:43:2:43:32 | ENCRYPT_WITH_RC20(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:44:2:44:39 | ENCRYPT_WITH_DES_REMOVED(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:49:2:49:26 | DES3ENCRYPT(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:54:2:54:26 | DES_SET_KEY(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:56:2:56:9 | DES(str) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:59:12:59:25 | SORT_ORDER_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:91:2:91:12 | call to encrypt3DES | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:92:2:92:17 | call to encryptTripleDES | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:97:2:97:12 | call to DES3Encrypt | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:102:2:102:12 | call to DES_Set_Key | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref new file mode 100644 index 00000000000..8424dee1a9b --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp new file mode 100644 index 00000000000..4a03b8a5261 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -0,0 +1,109 @@ + +typedef unsigned long size_t; + +// --- simple encryption macro invocations --- + +void my_implementation1(void *data, size_t amount); +void my_implementation2(void *data, size_t amount); +void my_implementation3(void *data, size_t amount); +void my_implementation4(void *data, size_t amount); +void my_implementation5(void *data, size_t amount); +void my_implementation6(const char *str); + +#define ENCRYPT_WITH_DES(data, amount) my_implementation1(data, amount) +#define ENCRYPT_WITH_RC2(data, amount) my_implementation2(data, amount) +#define ENCRYPT_WITH_AES(data, amount) my_implementation3(data, amount) +#define ENCRYPT_WITH_3DES(data, amount) my_implementation4(data, amount) +#define ENCRYPT_WITH_TRIPLE_DES(data, amount) my_implementation4(data, amount) +#define ENCRYPT_WITH_RC20(data, amount) my_implementation5(data, amount) +#define ENCRYPT_WITH_DES_REMOVED(data, amount) + +#define DESENCRYPT(data, amount) my_implementation1(data, amount) +#define RC2ENCRYPT(data, amount) my_implementation2(data, amount) +#define AESENCRYPT(data, amount) my_implementation3(data, amount) +#define DES3ENCRYPT(data, amount) my_implementation4(data, amount) + +#define DES_DO_ENCRYPTION(data, amount) my_implementation1(data, amount) +#define RUN_DES_ENCODING(data, amount) my_implementation1(data, amount) +#define DES_ENCODE(data, amount) my_implementation1(data, amount) +#define DES_SET_KEY(data, amount) my_implementation1(data, amount) + +#define DES(str) my_implementation6(str) +#define DESMOND(str) my_implementation6(str) +#define ANODES(str) my_implementation6(str) +#define SORT_ORDER_DES (1) + +void test_macros(void *data, size_t amount, const char *str) +{ + ENCRYPT_WITH_DES(data, amount); // BAD + ENCRYPT_WITH_RC2(data, amount); // BAD + ENCRYPT_WITH_AES(data, amount); // GOOD (good algorithm) + ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) [FALSE POSITIVE] + ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) [FALSE POSITIVE] + + DESENCRYPT(data, amount); // BAD [NOT DETECTED] + RC2ENCRYPT(data, amount); // BAD [NOT DETECTED] + AESENCRYPT(data, amount); // GOOD (good algorithm) + DES3ENCRYPT(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + + DES_DO_ENCRYPTION(data, amount); // BAD + RUN_DES_ENCODING(data, amount); // BAD + DES_ENCODE(data, amount); // BAD + DES_SET_KEY(data, amount); // BAD + + DES(str); // GOOD (probably nothing to do with encryption) [FALSE POSITIVE] + DESMOND(str); // GOOD (probably nothing to do with encryption) + ANODES(str); // GOOD (probably nothing to do with encryption) + int ord = SORT_ORDER_DES; // GOOD (probably nothing to do with encryption) [FALSE POSITIVE] +} + +// --- simple encryption function calls --- + +void encryptDES(void *data, size_t amount); +void encryptRC2(void *data, size_t amount); +void encryptAES(void *data, size_t amount); +void encrypt3DES(void *data, size_t amount); +void encryptTripleDES(void *data, size_t amount); + +void DESEncrypt(void *data, size_t amount); +void RC2Encrypt(void *data, size_t amount); +void AESEncrypt(void *data, size_t amount); +void DES3Encrypt(void *data, size_t amount); + +void DoDESEncryption(void *data, size_t amount); +void encryptDes(void *data, size_t amount); +void do_des_encrypt(void *data, size_t amount); +void DES_Set_Key(const char *key); +void DESSetKey(const char *key); + +int Des(); +void Desmond(const char *str); +void Anodes(int i); +void ConDes(); + +void test_functions(void *data, size_t amount, const char *str) +{ + encryptDES(data, amount); // BAD + encryptRC2(data, amount); // BAD + encryptAES(data, amount); // GOOD (good algorithm) + encrypt3DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + encryptTripleDES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + + DESEncrypt(data, amount); // BAD + RC2Encrypt(data, amount); // BAD + AESEncrypt(data, amount); // GOOD (good algorithm) + DES3Encrypt(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + + DoDESEncryption(data, amount); // BAD [NOT DETECTED] + encryptDes(data, amount); // BAD [NOT DETECTED] + do_des_encrypt(data, amount); // BAD + DES_Set_Key(str); // BAD + DESSetKey(str); // BAD [NOT DETECTED] + + Des(); // GOOD (probably nothing to do with encryption) + Desmond(str); // GOOD (probably nothing to do with encryption) + Anodes(1); // GOOD (probably nothing to do with encryption) + ConDes(); // GOOD (probably nothing to do with encryption) +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp new file mode 100644 index 00000000000..774d541950f --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -0,0 +1,254 @@ + +typedef unsigned long size_t; + +int strcmp(const char *s1, const char *s2); +void abort(void); + +struct keytype +{ + char data[16]; +}; + +void my_des_implementation(char *data, size_t amount, keytype key); +void my_rc2_implementation(char *data, size_t amount, keytype key); +void my_aes_implementation(char *data, size_t amount, keytype key); +void my_3des_implementation(char *data, size_t amount, keytype key); + +typedef void (*implementation_fn_ptr)(char *data, size_t amount, keytype key); + +// --- more involved C-style example --- + +#define ALGO_DES (1) +#define ALGO_AES (2) + +int all_algos[] = { + ALGO_DES, // [FALSE POSITIVE] + ALGO_AES +}; + +void encrypt_good(char *data, size_t amount, keytype key, int algo) +{ + switch (algo) + { + case ALGO_DES: // [FALSE POSITIVE] + abort(); + + case ALGO_AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +}; + +void encrypt_bad(char *data, size_t amount, keytype key, int algo) +{ + switch (algo) + { + case ALGO_DES: + { + my_des_implementation(data, amount, key); // BAD + } break; + + case ALGO_AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +}; + +void do_encrypts(char *data, size_t amount, keytype key) +{ + encrypt_good(data, amount, key, ALGO_AES); // GOOD + encrypt_bad(data, amount, key, ALGO_DES); // BAD +} + +// --- more involved CPP-style example --- + +enum algorithm +{ + DES, + AES +}; + +algorithm all_algorithms[] = { + DES, + AES +}; + +class MyGoodEncryptor +{ +public: + MyGoodEncryptor(keytype _key, algorithm _algo) : key(_key), algo(_algo) {}; + + void encrypt(char *data, size_t amount); + +private: + keytype key; + algorithm algo; +}; + +void MyGoodEncryptor :: encrypt(char *data, size_t amount) +{ + switch (algo) + { + case DES: + { + throw "DES is not a good choice of encryption algorithm!"; + } break; + + case AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +} + +class MyBadEncryptor +{ +public: + MyBadEncryptor(keytype _key, algorithm _algo) : key(_key), algo(_algo) {}; + + void encrypt(char *data, size_t amount); + +private: + keytype key; + algorithm algo; +}; + +void MyBadEncryptor :: encrypt(char *data, size_t amount) +{ + switch (algo) + { + case DES: + { + my_des_implementation(data, amount, key); // BAD + } break; + + case AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +} + +void do_class_encrypts(char *data, size_t amount, keytype key) +{ + { + MyGoodEncryptor mge(key, AES); // GOOD + + mge.encrypt(data, amount); + + } + + { + MyBadEncryptor mbe(key, DES); // BAD [NOT DETECTED] + + mbe.encrypt(data, amount); + } +} + +// --- unseen implementation --- + +enum use_algorithm +{ + USE_DES, + USE_AES +}; + +void set_encryption_algorithm1(int algorithm); +void set_encryption_algorithm2(use_algorithm algorithm); +void set_encryption_algorithm3(const char *algorithm_str); + +void encryption_with1(char *data, size_t amount, keytype key, int algorithm); +void encryption_with2(char *data, size_t amount, keytype key, use_algorithm algorithm); +void encryption_with3(char *data, size_t amount, keytype key, const char *algorithm_str); + +int get_algorithm1(); +use_algorithm get_algorithm2(); +const char *get_algorithm3(); + +void do_unseen_encrypts(char *data, size_t amount, keytype key) +{ + set_encryption_algorithm1(ALGO_DES); // BAD + set_encryption_algorithm1(ALGO_AES); // GOOD + + set_encryption_algorithm2(USE_DES); // BAD [NOT DETECTED] + set_encryption_algorithm2(USE_AES); // GOOD + + set_encryption_algorithm3("DES"); // BAD [NOT DETECTED] + set_encryption_algorithm3("AES"); // GOOD + set_encryption_algorithm3("AES-256"); // GOOD + + encryption_with1(data, amount, key, ALGO_DES); // BAD + encryption_with1(data, amount, key, ALGO_AES); // GOOD + + encryption_with2(data, amount, key, USE_DES); // BAD [NOT DETECTED] + encryption_with2(data, amount, key, USE_AES); // GOOD + + encryption_with3(data, amount, key, "DES"); // BAD [NOT DETECTED] + encryption_with3(data, amount, key, "AES"); // GOOD + encryption_with3(data, amount, key, "AES-256"); // GOOD + + if (get_algorithm1() == ALGO_DES) // GOOD [FALSE POSITIVE] + { + throw "DES is not a good choice of encryption algorithm!"; + } + if (get_algorithm2() == USE_DES) // GOOD + { + throw "DES is not a good choice of encryption algorithm!"; + } + if (strcmp(get_algorithm3(), "DES") == 0) // GOOD + { + throw "DES is not a good choice of encryption algorithm!"; + } +} + +// --- classes --- + +class desEncrypt +{ +public: + static void encrypt(const char *data); +}; + +class aes256Encrypt +{ +public: + static void encrypt(const char *data); +}; + +class desCipher +{ +public: + void encrypt(const char *data); +}; + +class aesCipher +{ +public: + void encrypt(const char *data); +}; + +void do_classes(const char *data) +{ + desEncrypt::encrypt(data); // BAD [NOT DETECTED] + aes256Encrypt::encrypt(data); // GOOD + + desCipher dc; + aesCipher ac; + dc.encrypt(data); // BAD [NOT DETECTED] + ac.encrypt(data); // GOOD +} + +// --- function pointer --- + +void do_fn_ptr(char *data, size_t amount, keytype key) +{ + implementation_fn_ptr impl; + + impl = &my_des_implementation; // BAD [NOT DETECTED] + impl(data, amount, key); + + impl = &my_aes_implementation; // GOOD + impl(data, amount, key); +} From b6d5f7c315b71b8ac135a53cf1796e86dd18fc03 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 12 May 2021 14:45:59 +0100 Subject: [PATCH 341/550] C++: Fix FPs caused by substring regexp. --- .../semmle/code/cpp/security/Encryption.qll | 34 ++++++++++--------- .../CWE-327/BrokenCryptoAlgorithm.expected | 5 --- .../query-tests/Security/CWE/CWE-327/test.cpp | 10 +++--- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 606242e833c..24c69f93758 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -27,14 +27,15 @@ string getAnInsecureHashAlgorithmName() { result = ["SHA1", "MD5"] } string getInsecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case switch, - // or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + - ")([^a-z].*|$)" + // alphabetical characters in the same case or numerical digits. This + // handles the upper case: + "(^|.*[^A-Z0-9])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by + //camelCase, hence we require two preceding uppercase letters to be + // sure of a case switch (or a preceding non-alphabetic, non-numeric + // character). + "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" + + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + ")([^a-z0-9].*|$)" } /** @@ -51,14 +52,15 @@ string getASecureAlgorithmName() { string getSecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case - // switch, or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + - ")([^a-z].*|$)" + // alphabetical characters in the same case or numerical digits. This + // handles the upper case: + "(^|.*[^A-Z0-9])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by + //camelCase, hence we require two preceding uppercase letters to be + // sure of a case switch (or a preceding non-alphabetic, non-numeric + // character). + "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + + ")([^a-z0-9].*|$)" } /** diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 47f6e1daa7e..f4ecaf8560f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -9,11 +9,8 @@ | test2.cpp:192:26:192:33 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:41:2:41:32 | ENCRYPT_WITH_3DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:43:2:43:32 | ENCRYPT_WITH_RC20(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:44:2:44:39 | ENCRYPT_WITH_DES_REMOVED(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:49:2:49:26 | DES3ENCRYPT(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | @@ -22,8 +19,6 @@ | test.cpp:59:12:59:25 | SORT_ORDER_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:91:2:91:12 | call to encrypt3DES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:92:2:92:17 | call to encryptTripleDES | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:97:2:97:12 | call to DES3Encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:102:2:102:12 | call to DES_Set_Key | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index 4a03b8a5261..da73b8b35fa 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -38,15 +38,15 @@ void test_macros(void *data, size_t amount, const char *str) ENCRYPT_WITH_DES(data, amount); // BAD ENCRYPT_WITH_RC2(data, amount); // BAD ENCRYPT_WITH_AES(data, amount); // GOOD (good algorithm) - ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] - ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) [FALSE POSITIVE] + ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) [FALSE POSITIVE] DESENCRYPT(data, amount); // BAD [NOT DETECTED] RC2ENCRYPT(data, amount); // BAD [NOT DETECTED] AESENCRYPT(data, amount); // GOOD (good algorithm) - DES3ENCRYPT(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + DES3ENCRYPT(data, amount); // GOOD (good enough algorithm) DES_DO_ENCRYPTION(data, amount); // BAD RUN_DES_ENCODING(data, amount); // BAD @@ -88,13 +88,13 @@ void test_functions(void *data, size_t amount, const char *str) encryptDES(data, amount); // BAD encryptRC2(data, amount); // BAD encryptAES(data, amount); // GOOD (good algorithm) - encrypt3DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + encrypt3DES(data, amount); // GOOD (good enough algorithm) encryptTripleDES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] DESEncrypt(data, amount); // BAD RC2Encrypt(data, amount); // BAD AESEncrypt(data, amount); // GOOD (good algorithm) - DES3Encrypt(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + DES3Encrypt(data, amount); // GOOD (good enough algorithm) DoDESEncryption(data, amount); // BAD [NOT DETECTED] encryptDes(data, amount); // BAD [NOT DETECTED] From 9404d0676dc6a7ab4b8a3216cb65c07f8e627c19 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 12 May 2021 14:51:47 +0100 Subject: [PATCH 342/550] C++: Exclude macros that don't generate anything. --- .../Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 12 ++++++++++-- .../CWE/CWE-327/BrokenCryptoAlgorithm.expected | 1 - .../test/query-tests/Security/CWE/CWE-327/test.cpp | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index af64a1789c3..b8f86381216 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -23,7 +23,10 @@ Function getAnInsecureFunction() { } class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { - InsecureFunctionCall() { this.getTarget() = getAnInsecureFunction() } + InsecureFunctionCall() { + // the function name suggests it relates to an insecure crypto algorithm. + this.getTarget() = getAnInsecureFunction() + } override string description() { result = "function call" } @@ -38,7 +41,12 @@ Macro getAnInsecureMacro() { } class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { - InsecureMacroSpec() { this.getMacro() = getAnInsecureMacro() } + InsecureMacroSpec() { + // the macro name suggests it relates to an insecure crypto algorithm. + this.getMacro() = getAnInsecureMacro() and + // the macro invocation generates something. + exists(this.getAGeneratedElement()) + } override string description() { result = "macro invocation" } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index f4ecaf8560f..62fdec370ce 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -10,7 +10,6 @@ | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:44:2:44:39 | ENCRYPT_WITH_DES_REMOVED(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index da73b8b35fa..2fbd9ae73d5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -41,7 +41,7 @@ void test_macros(void *data, size_t amount, const char *str) ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) - ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) [FALSE POSITIVE] + ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) DESENCRYPT(data, amount); // BAD [NOT DETECTED] RC2ENCRYPT(data, amount); // BAD [NOT DETECTED] From 52a88af6c1e7f2ffbb51dfc3fe8f499ca95ba189 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 12 May 2021 14:58:21 +0100 Subject: [PATCH 343/550] C++: Exclude macro invocations in switch case expressions. --- cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 5 ++++- .../Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected | 3 --- cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index b8f86381216..296f6bec3f8 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -45,7 +45,10 @@ class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { // the macro name suggests it relates to an insecure crypto algorithm. this.getMacro() = getAnInsecureMacro() and // the macro invocation generates something. - exists(this.getAGeneratedElement()) + exists(this.getAGeneratedElement().(ControlFlowNode)) and + // exclude expressions controlling ifs/switches (as they may not be used). + not any(IfStmt c).getCondition().getAChild*() = this.getAGeneratedElement() and + not any(SwitchCase c).getExpr().getAChild*() = this.getAGeneratedElement() } override string description() { result = "macro invocation" } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 62fdec370ce..35be30d162c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,12 +1,9 @@ | test2.cpp:25:2:25:9 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test2.cpp:33:7:33:14 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test2.cpp:47:7:47:14 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:62:33:62:40 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:172:28:172:35 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test2.cpp:192:26:192:33 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp index 774d541950f..80e8691426c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -30,7 +30,7 @@ void encrypt_good(char *data, size_t amount, keytype key, int algo) { switch (algo) { - case ALGO_DES: // [FALSE POSITIVE] + case ALGO_DES: abort(); case ALGO_AES: @@ -189,7 +189,7 @@ void do_unseen_encrypts(char *data, size_t amount, keytype key) encryption_with3(data, amount, key, "AES"); // GOOD encryption_with3(data, amount, key, "AES-256"); // GOOD - if (get_algorithm1() == ALGO_DES) // GOOD [FALSE POSITIVE] + if (get_algorithm1() == ALGO_DES) // GOOD { throw "DES is not a good choice of encryption algorithm!"; } From 0450caa73df21b92914b4633ceba4f613aea8b74 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 12 May 2021 19:39:30 +0100 Subject: [PATCH 344/550] C++: Exclude array initializers. --- cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 4 +++- .../Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected | 1 - cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 296f6bec3f8..3015528114d 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -48,7 +48,9 @@ class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { exists(this.getAGeneratedElement().(ControlFlowNode)) and // exclude expressions controlling ifs/switches (as they may not be used). not any(IfStmt c).getCondition().getAChild*() = this.getAGeneratedElement() and - not any(SwitchCase c).getExpr().getAChild*() = this.getAGeneratedElement() + not any(SwitchCase c).getExpr().getAChild*() = this.getAGeneratedElement() and + // exclude expressions in array initializers (as they may not be used). + not any(AggregateLiteral i).getAChild*() = this.getAGeneratedElement() } override string description() { result = "macro invocation" } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 35be30d162c..8b17f1a83b7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,4 +1,3 @@ -| test2.cpp:25:2:25:9 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:62:33:62:40 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp index 80e8691426c..622db3aff0e 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -22,7 +22,7 @@ typedef void (*implementation_fn_ptr)(char *data, size_t amount, keytype key); #define ALGO_AES (2) int all_algos[] = { - ALGO_DES, // [FALSE POSITIVE] + ALGO_DES, ALGO_AES }; From fe12e620dde570aba2fea1746b460e61707c040d Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 18:37:42 +0000 Subject: [PATCH 345/550] Python: Avoid clobbering `range` in test This was an unwanted interaction between two unrelated tests, so I switched to a different built-in in the second test. I also added a test case that shows an unfortunate side effect of this more restricted handling of built-ins. --- .../experimental/dataflow/ApiGraphs/test.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/python/ql/test/experimental/dataflow/ApiGraphs/test.py b/python/ql/test/experimental/dataflow/ApiGraphs/test.py index 5a382f3d6cc..f4988e41a0f 100644 --- a/python/ql/test/experimental/dataflow/ApiGraphs/test.py +++ b/python/ql/test/experimental/dataflow/ApiGraphs/test.py @@ -122,14 +122,18 @@ def redefine_print(): print = my_print print("these words") -def local_redefine_range(): - range = 5 - return range +def local_redefine_chr(): + chr = 5 + return chr -def global_redefine_range(): - global range - range = 6 - return range #$ SPURIOUS: use=moduleImport("builtins").getMember("range") +def global_redefine_chr(): + global chr + chr = 6 + return chr + +def what_is_chr_now(): + # If global_redefine_chr has been run, then the following is _not_ a reference to the built-in chr + return chr(123) #$ MISSING: use=moduleImport("builtins").getMember("chr").getReturn() def obscured_print(): p = print #$ use=moduleImport("builtins").getMember("print") From 2efa0ad1055ca58ef492c639aa2b40c4c83dcb98 Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Wed, 12 May 2021 22:36:24 +0300 Subject: [PATCH 346/550] [C++] Implement module ClickHouseDriver.qll --- .../CWE-089/ClickHouseSQLInjection.py | 28 ++++++ .../CWE-089/ClickHouseSQLInjection.qhelp | 57 +++++++++++++ .../CWE-089/ClickHouseSQLInjection.ql | 22 +++++ .../python/frameworks/ClickHouseDriver.qll | 85 +++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py create mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp create mode 100644 python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql create mode 100644 python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py new file mode 100644 index 00000000000..c6a2875df23 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py @@ -0,0 +1,28 @@ +from django.conf.urls import url +from clickhouse_driver import Client +from clickhouse_driver import connect +from aioch import Client as aiochClient + +def show_user(request, username): + + # BAD -- async library 'aioch' + aclient = aiochClient("localhost") + progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username) + + # BAD -- client excute + client = Client('localhost') + client.execute("SELECT * FROM users WHERE username = '%s'" % username) + + # BAD -- client excute oneliner + Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username) + + # GOOD -- send username through params + query = "SELECT * FROM users WHERE username = %(username)s" + Client('localhost').execute(query, {"username": username}) + + # BAD -- PEP249 interface + conn = connect('clickhouse://localhost') + cursor = conn.cursor() + cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) + +urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)] diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp new file mode 100644 index 00000000000..42edf58cd6e --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp @@ -0,0 +1,57 @@ + + + + +

    +If a database query (such as a SQL or NoSQL query) is built from +user-provided data without sufficient sanitization, a user +may be able to run malicious database queries. +

    +
    + + +

    +Most database connector libraries offer a way of safely +embedding untrusted data into a query by means of query parameters +or prepared statements. +

    +
    + + +

    +In the following snippet, a user is fetched from the ClickHouse database +using five different queries. In bad patterns query is build by formatting +string with user-controlled data. In a qood pattern user-supplied parameter +is send through a second argument (params) which is dict. Following cases +show different types of communication with ClickHouse database. +

    + +

    +In the first case, the query executed via aioch Client. aioch - is a module +for asynchronous queries to database. +

    + +

    +In the second and third cases, the connection is established via `Client` class. +This class implement different method to execute a query. +

    + +

    +In the forth case, good pattern is presented. Query parameters are send through +second dict-like argument. +

    + +

    +In the fifth case, there is example of PEP249 interface usage. +

    + + +
    + + +
  • Wikipedia: SQL injection.
  • +
  • OWASP: SQL Injection Prevention Cheat Sheet.
  • +
    +
    diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql new file mode 100644 index 00000000000..f0efb523756 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.ql @@ -0,0 +1,22 @@ +/** + * @id py/yandex/clickhouse-sql-injection + * @name Clickhouse SQL query built from user-controlled sources + * @description Building a SQL query from user-controlled sources is vulnerable to insertion of + * malicious SQL code by the user. + * @kind path-problem + * @problem.severity error + * @precision high + * @tags security + * external/cwe/cwe-089 + * external/owasp/owasp-a1 + */ + +import python +import experimental.semmle.python.frameworks.ClickHouseDriver +import semmle.python.security.dataflow.SqlInjection +import DataFlow::PathGraph + +from SQLInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This SQL query depends on $@.", source.getNode(), + "a user-provided value" diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll new file mode 100644 index 00000000000..592e003d35b --- /dev/null +++ b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll @@ -0,0 +1,85 @@ +/** + * Provides classes modeling security-relevant aspects of `clickhouse-driver` and `aioch` PyPI packages. + * See + * - https://pypi.org/project/clickhouse-driver/ + * - https://pypi.org/project/aioch/ + * - https://clickhouse-driver.readthedocs.io/en/latest/ + */ + +private import python +private import semmle.python.Concepts +private import semmle.python.ApiGraphs +private import semmle.python.frameworks.PEP249 + +/** + * Provides models for `clickhouse-driver` and `aioch` PyPI packages. + * See + * - https://pypi.org/project/clickhouse-driver/ + * - https://pypi.org/project/aioch/ + * - https://clickhouse-driver.readthedocs.io/en/latest/ + */ +module ClickHouseDriver { + /** Gets a reference to the `clickhouse_driver` module. */ + API::Node clickhouse_driver() { result = API::moduleImport("clickhouse_driver") } + + /** Gets a reference to the `aioch` module. This module allows to make async db queries. */ + API::Node aioch() { result = API::moduleImport("aioch") } + + /** + * `clickhouse_driver` implements PEP249, + * providing ways to execute SQL statements against a database. + */ + class ClickHouseDriverPEP249 extends PEP249ModuleApiNode { + ClickHouseDriverPEP249() { this = clickhouse_driver() } + } + + module Client { + /** Gets a reference to a Client call. */ + private DataFlow::Node client_ref() { + result = clickhouse_driver().getMember("Client").getAUse() + or + result = aioch().getMember("Client").getAUse() + } + + /** A direct instantiation of `clickhouse_driver.Client`. */ + private class ClientInstantiation extends DataFlow::CallCfgNode { + ClientInstantiation() { this.getFunction() = client_ref() } + } + + /** Gets a reference to an instance of `clickhouse_driver.Client`. */ + private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) { + t.start() and + result instanceof ClientInstantiation + or + exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t)) + } + + /** Gets a reference to an instance of `clickhouse_driver.Client`. */ + DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) } + } + + /** clickhouse_driver.Client execute methods */ + private string execute_function() { + result in ["execute_with_progress", "execute", "execute_iter"] + } + + /** Gets a reference to the `clickhouse_driver.Client.execute` method */ + private DataFlow::LocalSourceNode clickhouse_execute(DataFlow::TypeTracker t) { + t.startInAttr(execute_function()) and + result = Client::instance() + or + exists(DataFlow::TypeTracker t2 | result = clickhouse_execute(t2).track(t2, t)) + } + + /** Gets a reference to the `clickhouse_driver.Client.execute` method */ + DataFlow::Node clickhouse_execute() { + clickhouse_execute(DataFlow::TypeTracker::end()).flowsTo(result) + } + + /** A call to the `clickhouse_driver.Client.execute` method */ + private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode { + ExecuteCall() { this.getFunction() = clickhouse_execute() } + + override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) } + } +} From 34fbafafde8be89e1383b378c052cbcdcdcc7000 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 12 May 2021 22:34:44 +0200 Subject: [PATCH 347/550] remove redundant "put" case --- .../ql/src/semmle/javascript/frameworks/ClientRequests.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll index bce5d6a36fc..18204c5b59b 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/ClientRequests.qll @@ -242,7 +242,7 @@ module ClientRequest { method = "request" and result = getOptionArgument(0, "data") or - method = ["post", "put", "put"] and + method = ["post", "put"] and result = [getArgument(1), getOptionArgument(2, "data")] or result = getOptionArgument([0 .. 2], ["headers", "params"]) From 470e3eb08924f3b475cec215c9905d3603778280 Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Thu, 13 May 2021 00:03:53 +0300 Subject: [PATCH 348/550] [python] ClickHouseDriver.qll: add support for subclasses --- .../Security/CWE-089/ClickHouseSQLInjection.py | 7 +++++++ .../Security/CWE-089/ClickHouseSQLInjection.qhelp | 4 ++++ .../semmle/python/frameworks/ClickHouseDriver.qll | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py index c6a2875df23..0bd5cac7130 100644 --- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py +++ b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py @@ -3,6 +3,10 @@ from clickhouse_driver import Client from clickhouse_driver import connect from aioch import Client as aiochClient +class MyClient(Client): + def dummy(self): + return None + def show_user(request, username): # BAD -- async library 'aioch' @@ -25,4 +29,7 @@ def show_user(request, username): cursor = conn.cursor() cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) + # BAD -- MyClient is a subclass of Client + MyClient('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username) + urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)] diff --git a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp index 42edf58cd6e..a7077727991 100644 --- a/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp +++ b/python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp @@ -47,6 +47,10 @@ second dict-like argument. In the fifth case, there is example of PEP249 interface usage.

    +

    +In the sixth case, there is custom Class usge which is a subclass of default Client. +

    + diff --git a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll index 592e003d35b..c456b6bdb89 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/ClickHouseDriver.qll @@ -36,9 +36,9 @@ module ClickHouseDriver { module Client { /** Gets a reference to a Client call. */ private DataFlow::Node client_ref() { - result = clickhouse_driver().getMember("Client").getAUse() + result = clickhouse_driver().getMember("Client").getASubclass*().getAUse() or - result = aioch().getMember("Client").getAUse() + result = aioch().getMember("Client").getASubclass*().getAUse() } /** A direct instantiation of `clickhouse_driver.Client`. */ From fad55b3635994ab25925548b62939e355d6eb474 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 21:09:51 +0000 Subject: [PATCH 349/550] Python: Reimplement `py/use-of-input` --- python/ql/src/Expressions/UseofInput.ql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql index 2b11eecfa2b..db905818276 100644 --- a/python/ql/src/Expressions/UseofInput.ql +++ b/python/ql/src/Expressions/UseofInput.ql @@ -11,11 +11,11 @@ */ import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.ApiGraphs -from CallNode call, Context context, ControlFlowNode func +from DataFlow::CallCfgNode call where - context.getAVersion().includes(2, _) and - call.getFunction() = func and - func.pointsTo(context, Value::named("input"), _) and - not func.pointsTo(context, Value::named("raw_input"), _) + call = API::builtin("input").getACall() and + call != API::builtin("raw_input").getACall() select call, "The unsafe built-in function 'input' is used in Python 2." From 79cfe5aca2b19193a1adce28e0cead35fad996a0 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 12 May 2021 21:23:16 +0000 Subject: [PATCH 350/550] Python: Limit `py/use-of-input` to Python 2 --- python/ql/src/Expressions/UseofInput.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/python/ql/src/Expressions/UseofInput.ql b/python/ql/src/Expressions/UseofInput.ql index db905818276..bf2a71de22e 100644 --- a/python/ql/src/Expressions/UseofInput.ql +++ b/python/ql/src/Expressions/UseofInput.ql @@ -16,6 +16,7 @@ import semmle.python.ApiGraphs from DataFlow::CallCfgNode call where + major_version() = 2 and call = API::builtin("input").getACall() and call != API::builtin("raw_input").getACall() select call, "The unsafe built-in function 'input' is used in Python 2." From effa2b162a77031186bf26114e2e6e0e6e847154 Mon Sep 17 00:00:00 2001 From: haby0 Date: Thu, 6 May 2021 12:05:26 +0800 Subject: [PATCH 351/550] Add spring url redirection detect --- .../CWE/CWE-601/SpringUrlRedirect.java | 46 +++++++ .../CWE/CWE-601/SpringUrlRedirect.qhelp | 37 +++++ .../Security/CWE/CWE-601/SpringUrlRedirect.ql | 42 ++++++ .../CWE/CWE-601/SpringUrlRedirect.qll | 91 ++++++++++++ .../CWE-601/SpringUrlRedirect.expected | 19 +++ .../security/CWE-601/SpringUrlRedirect.java | 52 +++++++ .../security/CWE-601/SpringUrlRedirect.qlref | 1 + .../query-tests/security/CWE-601/options | 1 + .../web/servlet/ModelAndView.java | 107 +++++++++++++++ .../org/springframework/web/servlet/View.java | 20 +++ .../servlet/view/AbstractUrlBasedView.java | 39 ++++++ .../web/servlet/view/RedirectView.java | 129 ++++++++++++++++++ 12 files changed, 584 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-601/options create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java create mode 100644 java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java new file mode 100644 index 00000000000..eba64aab6a8 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.java @@ -0,0 +1,46 @@ +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +@Controller +public class SpringUrlRedirect { + + private final static String VALID_REDIRECT = "http://127.0.0.1"; + + @GetMapping("url1") + public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception { + RedirectView rv = new RedirectView(); + rv.setUrl(redirectUrl); + return rv; + } + + @GetMapping("url2") + public String bad2(String redirectUrl) { + String url = "redirect:" + redirectUrl; + return url; + } + + @GetMapping("url3") + public RedirectView bad3(String redirectUrl) { + RedirectView rv = new RedirectView(redirectUrl); + return rv; + } + + @GetMapping("url4") + public ModelAndView bad4(String redirectUrl) { + return new ModelAndView("redirect:" + redirectUrl); + } + + @GetMapping("url5") + public RedirectView good1(String redirectUrl) { + RedirectView rv = new RedirectView(); + if (redirectUrl.startsWith(VALID_REDIRECT)){ + rv.setUrl(redirectUrl); + }else { + rv.setUrl(VALID_REDIRECT); + } + return rv; + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp new file mode 100644 index 00000000000..6fe70dfb113 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qhelp @@ -0,0 +1,37 @@ + + + + + +

    Directly incorporating user input into a URL redirect request without validating the input +can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a +malicious site that looks very similar to the real site they intend to visit, but which is +controlled by the attacker.

    + +
    + + +

    To guard against untrusted URL redirection, it is advisable to avoid putting user input +directly into a redirect URL. Instead, maintain a list of authorized +redirects on the server; then choose from that list based on the user input provided.

    + +
    + + +

    The following examples show the bad case and the good case respectively. +In bad1 method and bad2 method and bad3 method and +bad4 method, shows an HTTP request parameter being used directly in a URL redirect +without validating the input, which facilitates phishing attacks. In good1 method, +shows how to solve this problem by verifying whether the user input is a known fixed string beginning. +

    + + + +
    + +
  • A Guide To Spring Redirects: Spring Redirects.
  • +
  • Url redirection - attack and defense: Url Redirection.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql new file mode 100644 index 00000000000..138bce57ac9 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -0,0 +1,42 @@ +/** + * @name Spring url redirection from remote source + * @description Spring url redirection based on unvalidated user-input + * may cause redirection to malicious web sites. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/spring-unvalidated-url-redirection + * @tags security + * external/cwe-601 + */ + +import java +import SpringUrlRedirect +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { + SpringUrlRedirectFlowConfig() { this = "SpringUrlRedirectFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink } + + override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { + guard instanceof StartsWithSanitizer + } + + override predicate isSanitizer(DataFlow::Node node) { + // Exclude the case where the left side of the concatenated string is not `redirect:`. + // E.g: `String url = "/path?token=" + request.getParameter("token");` + exists(AddExpr ae | + ae.getRightOperand() = node.asExpr() and + not ae instanceof RedirectBuilderExpr + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, SpringUrlRedirectFlowConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potentially untrusted URL redirection due to $@.", + source.getNode(), "user-provided value" diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll new file mode 100644 index 00000000000..0ea88e84673 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -0,0 +1,91 @@ +import java +import DataFlow +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.DataFlow2 +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.frameworks.spring.SpringController + +class StartsWithSanitizer extends DataFlow::BarrierGuard { + StartsWithSanitizer() { + this.(MethodAccess).getMethod().hasName("startsWith") and + this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and + this.(MethodAccess).getMethod().getNumberOfParameters() = 1 + } + + override predicate checks(Expr e, boolean branch) { + e = this.(MethodAccess).getQualifier() and branch = true + } +} + +/** + * A concatenate expression using the string `redirect:` on the left. + * + * E.g: `"redirect:" + redirectUrl` + */ +class RedirectBuilderExpr extends AddExpr { + RedirectBuilderExpr() { + this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "redirect:" + } +} + +/** A URL redirection sink from spring controller method. */ +class SpringUrlRedirectSink extends DataFlow::Node { + SpringUrlRedirectSink() { + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + or + exists(MethodAccess ma | + ma.getMethod().hasName("setUrl") and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and + ma.getArgument(0) = this.asExpr() and + exists(RedirectViewFlowConfig rvfc | rvfc.hasFlowToExpr(ma.getQualifier())) + ) + or + exists(ClassInstanceExpr cie | + cie.getConstructedType() + .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and + cie.getArgument(0) = this.asExpr() + ) + or + exists(ClassInstanceExpr cie | + cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and + cie.getArgument(0) = this.asExpr() and + exists(RedirectBuilderFlowConfig rstrbfc | rstrbfc.hasFlowToExpr(cie.getArgument(0))) + ) + } +} + +/** A data flow configuration tracing flow from remote sources to redirect builder expression. */ +private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { + RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } + + override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = sink.asExpr()) + } +} + +/** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ +private class RedirectViewFlowConfig extends DataFlow2::Configuration { + RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } + + override predicate isSource(DataFlow::Node src) { + exists(ClassInstanceExpr cie | + cie.getConstructedType() + .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and + cie = src.asExpr() + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | + ma.getMethod().hasName("setUrl") and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and + ma.getQualifier() = sink.asExpr() + ) + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected new file mode 100644 index 00000000000..fee0598bbee --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -0,0 +1,19 @@ +edges +| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | +| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | +| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | +| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | +nodes +| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl | +#select +| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java new file mode 100644 index 00000000000..1438b0a63a1 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -0,0 +1,52 @@ +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; + +@Controller +public class SpringUrlRedirect { + + private final static String VALID_REDIRECT = "http://127.0.0.1"; + + @GetMapping("url1") + public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception { + RedirectView rv = new RedirectView(); + rv.setUrl(redirectUrl); + return rv; + } + + @GetMapping("url2") + public String bad2(String redirectUrl) { + String url = "redirect:" + redirectUrl; + return url; + } + + @GetMapping("url3") + public RedirectView bad3(String redirectUrl) { + RedirectView rv = new RedirectView(redirectUrl); + return rv; + } + + @GetMapping("url4") + public ModelAndView bad4(String redirectUrl) { + return new ModelAndView("redirect:" + redirectUrl); + } + + @GetMapping("url5") + public RedirectView good1(String redirectUrl) { + RedirectView rv = new RedirectView(); + if (redirectUrl.startsWith(VALID_REDIRECT)){ + rv.setUrl(redirectUrl); + }else { + rv.setUrl(VALID_REDIRECT); + } + return rv; + } + + @GetMapping("url6") + public ModelAndView good2(String token) { + String url = "/edit?token=" + token; + return new ModelAndView("redirect:" + url); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref new file mode 100644 index 00000000000..418be1d307b --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/options b/java/ql/test/experimental/query-tests/security/CWE-601/options new file mode 100644 index 00000000000..a9289108747 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-601/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/ \ No newline at end of file diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java new file mode 100644 index 00000000000..53e337d5053 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/ModelAndView.java @@ -0,0 +1,107 @@ +package org.springframework.web.servlet; + +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; + +public class ModelAndView { + @Nullable + private Object view; + @Nullable + private HttpStatus status; + private boolean cleared = false; + + public ModelAndView() { + } + + public ModelAndView(String viewName) { + this.view = viewName; + } + + public ModelAndView(View view) { + this.view = view; + } + + public ModelAndView(String viewName, @Nullable Map model) { } + + public ModelAndView(View view, @Nullable Map model) { } + + public ModelAndView(String viewName, HttpStatus status) { } + + public ModelAndView(@Nullable String viewName, @Nullable Map model, @Nullable HttpStatus status) { } + + public ModelAndView(String viewName, String modelName, Object modelObject) { } + + public ModelAndView(View view, String modelName, Object modelObject) { } + + public void setViewName(@Nullable String viewName) { + this.view = viewName; + } + + @Nullable + public String getViewName() { + return ""; + } + + public void setView(@Nullable View view) { } + + @Nullable + public View getView() { + return null; + } + + public boolean hasView() { + return true; + } + + public boolean isReference() { + return true; + } + + @Nullable + protected Map getModelInternal() { + return null; + } + + public Map getModel() { + return null; + } + + public void setStatus(@Nullable HttpStatus status) { } + + @Nullable + public HttpStatus getStatus() { + return this.status; + } + + public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) { + return null; + } + + public ModelAndView addObject(Object attributeValue) { + return null; + } + + public ModelAndView addAllObjects(@Nullable Map modelMap) { + return null; + } + + public void clear() { } + + public boolean isEmpty() { + return true; + } + + public boolean wasCleared() { + return true; + } + + public String toString() { + return ""; + } + + private String formatView() { + return ""; + } +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java new file mode 100644 index 00000000000..b2281b8c250 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/View.java @@ -0,0 +1,20 @@ +package org.springframework.web.servlet; + +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.lang.Nullable; + +public interface View { + String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; + String PATH_VARIABLES = View.class.getName() + ".pathVariables"; + String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; + + @Nullable + default String getContentType() { + return null; + } + + void render(@Nullable Map var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception; +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java new file mode 100644 index 00000000000..9efd87af12f --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/AbstractUrlBasedView.java @@ -0,0 +1,39 @@ +package org.springframework.web.servlet.view; + +import java.util.Locale; +import org.springframework.lang.Nullable; + +public abstract class AbstractUrlBasedView { + @Nullable + private String url; + + protected AbstractUrlBasedView() { } + + protected AbstractUrlBasedView(String url) { + this.url = url; + } + + public void setUrl(@Nullable String url) { + this.url = url; + } + + @Nullable + public String getUrl() { + return ""; + } + + public void afterPropertiesSet() throws Exception { } + + protected boolean isUrlRequired() { + return true; + } + + public boolean checkResource(Locale locale) throws Exception { + return true; + } + + public String toString() { + return ""; + } +} + diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java new file mode 100644 index 00000000000..ee18868231a --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/web/servlet/view/RedirectView.java @@ -0,0 +1,129 @@ +package org.springframework.web.servlet.view; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.net.URLEncoder; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; + +public class RedirectView extends AbstractUrlBasedView { + private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}"); + private boolean contextRelative = false; + private boolean http10Compatible = true; + private boolean exposeModelAttributes = true; + @Nullable + private String encodingScheme; + @Nullable + private HttpStatus statusCode; + private boolean expandUriTemplateVariables = true; + private boolean propagateQueryParams = false; + @Nullable + private String[] hosts; + + public RedirectView() { } + + public RedirectView(String url) { } + + public RedirectView(String url, boolean contextRelative) { } + + public RedirectView(String url, boolean contextRelative, boolean http10Compatible) { } + + public RedirectView(String url, boolean contextRelative, boolean http10Compatible, boolean exposeModelAttributes) { } + + public void setContextRelative(boolean contextRelative) { } + + public void setHttp10Compatible(boolean http10Compatible) { } + + public void setExposeModelAttributes(boolean exposeModelAttributes) { } + + public void setEncodingScheme(String encodingScheme) { } + + public void setStatusCode(HttpStatus statusCode) { } + + public void setExpandUriTemplateVariables(boolean expandUriTemplateVariables) { } + + public void setPropagateQueryParams(boolean propagateQueryParams) { } + + public boolean isPropagateQueryProperties() { + return true; + } + + public void setHosts(@Nullable String... hosts) { } + + @Nullable + public String[] getHosts() { + return this.hosts; + } + + public boolean isRedirectView() { + return true; + } + + protected boolean isContextRequired() { + return false; + } + + protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws IOException { } + + protected final String createTargetUrl(Map model, HttpServletRequest request) throws UnsupportedEncodingException { + return ""; + } + + private String getContextPath(HttpServletRequest request) { + return ""; + } + + protected StringBuilder replaceUriTemplateVariables(String targetUrl, Map model, Map currentUriVariables, String encodingScheme) throws UnsupportedEncodingException { + return null; + } + + private Map getCurrentRequestUriVariables(HttpServletRequest request) { + return null; + } + + protected void appendCurrentQueryParams(StringBuilder targetUrl, HttpServletRequest request) { } + + protected void appendQueryProperties(StringBuilder targetUrl, Map model, String encodingScheme) throws UnsupportedEncodingException { } + + protected Map queryProperties(Map model) { + return null; + } + + protected boolean isEligibleProperty(String key, @Nullable Object value) { + return true; + } + + protected boolean isEligibleValue(@Nullable Object value) { + return true; + } + + protected String urlEncode(String input, String encodingScheme) throws UnsupportedEncodingException { + return ""; + } + + protected String updateTargetUrl(String targetUrl, Map model, HttpServletRequest request, HttpServletResponse response) { + return ""; + } + + protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException { } + + protected boolean isRemoteHost(String targetUrl) { + return true; + } + + protected HttpStatus getHttp11StatusCode(HttpServletRequest request, HttpServletResponse response, String targetUrl) { + return this.statusCode; + } +} + From 40cf29b62521ada08c8dbfeffa28027c1f283045 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 08:38:58 +0100 Subject: [PATCH 352/550] C++: Rearrange the library. --- .../Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 4 ++-- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 3015528114d..f1195a43736 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -18,7 +18,7 @@ abstract class InsecureCryptoSpec extends Locatable { } Function getAnInsecureFunction() { - result.getName().regexpMatch(getInsecureAlgorithmRegex()) and + isInsecureEncryption(result.getName()) and exists(result.getACallToThisFunction()) } @@ -36,7 +36,7 @@ class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { } Macro getAnInsecureMacro() { - result.getName().regexpMatch(getInsecureAlgorithmRegex()) and + isInsecureEncryption(result.getName()) and exists(result.getAnInvocation()) } diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 24c69f93758..22ca62372fe 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -14,6 +14,13 @@ string getAnInsecureAlgorithmName() { ] } +/** + * Gets the name of an algorithm that is known to be secure. + */ +string getASecureAlgorithmName() { + result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"] +} + /** * Gets the name of a hash algorithm that is insecure if it is being used for * encryption (but it is hard to know when that is happening). @@ -39,10 +46,11 @@ string getInsecureAlgorithmRegex() { } /** - * Gets the name of an algorithm that is known to be secure. + * Holds if `name` looks like it might be related to operations with an + * insecure encyption algorithm. */ -string getASecureAlgorithmName() { - result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"] +bindingset[name] predicate isInsecureEncryption(string name) { + name.regexpMatch(getInsecureAlgorithmRegex()) } /** From 02e415045f04957a33049907bd3256ce84ad7378 Mon Sep 17 00:00:00 2001 From: haby0 Date: Thu, 13 May 2021 15:48:15 +0800 Subject: [PATCH 353/550] Delete RedirectBuilderFlowConfig --- .../Security/CWE/CWE-601/SpringUrlRedirect.qll | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 0ea88e84673..1ab5f3cd0b1 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -51,22 +51,11 @@ class SpringUrlRedirectSink extends DataFlow::Node { exists(ClassInstanceExpr cie | cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and cie.getArgument(0) = this.asExpr() and - exists(RedirectBuilderFlowConfig rstrbfc | rstrbfc.hasFlowToExpr(cie.getArgument(0))) + exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) ) } } -/** A data flow configuration tracing flow from remote sources to redirect builder expression. */ -private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { - RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } - - override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = sink.asExpr()) - } -} - /** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ private class RedirectViewFlowConfig extends DataFlow2::Configuration { RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } From 123889a6716c16aee3febcbf820348f0c92c2e2d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 09:15:58 +0100 Subject: [PATCH 354/550] C++: Fix 'triple DES' false positives. --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 11 +++++++++-- .../CWE/CWE-327/BrokenCryptoAlgorithm.expected | 2 -- cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 22ca62372fe..d2261df6bc8 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -30,6 +30,9 @@ string getAnInsecureHashAlgorithmName() { result = ["SHA1", "MD5"] } /** * Gets the regular expression used for matching strings that look like they * contain an algorithm that is known to be insecure. + * + * Consider using `isInsecureEncryption` rather than accessing this regular + * expression directly. */ string getInsecureAlgorithmRegex() { result = @@ -49,8 +52,12 @@ string getInsecureAlgorithmRegex() { * Holds if `name` looks like it might be related to operations with an * insecure encyption algorithm. */ -bindingset[name] predicate isInsecureEncryption(string name) { - name.regexpMatch(getInsecureAlgorithmRegex()) +bindingset[name] +predicate isInsecureEncryption(string name) { + name.regexpMatch(getInsecureAlgorithmRegex()) and + // Check for evidence that an otherwise matching name may in fact not be + // related to insecure encrpytion, e.g. "Triple-DES" is not "DES". + not name.toUpperCase().regexpMatch(".*TRIPLE.*") } /** diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 8b17f1a83b7..53e9054a507 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -5,7 +5,6 @@ | test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | @@ -14,6 +13,5 @@ | test.cpp:59:12:59:25 | SORT_ORDER_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:92:2:92:17 | call to encryptTripleDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:102:2:102:12 | call to DES_Set_Key | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index 2fbd9ae73d5..8d16e5ed98e 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -39,7 +39,7 @@ void test_macros(void *data, size_t amount, const char *str) ENCRYPT_WITH_RC2(data, amount); // BAD ENCRYPT_WITH_AES(data, amount); // GOOD (good algorithm) ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) - ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) @@ -89,7 +89,7 @@ void test_functions(void *data, size_t amount, const char *str) encryptRC2(data, amount); // BAD encryptAES(data, amount); // GOOD (good algorithm) encrypt3DES(data, amount); // GOOD (good enough algorithm) - encryptTripleDES(data, amount); // GOOD (good enough algorithm) [FALSE POSITIVE] + encryptTripleDES(data, amount); // GOOD (good enough algorithm) DESEncrypt(data, amount); // BAD RC2Encrypt(data, amount); // BAD From e4d2c7cfc423ebd263bbc767c4ca829eb002b982 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 10:04:57 +0100 Subject: [PATCH 355/550] C++: Rewrite so that we look for additional evidence. --- .../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 94 +++++++++++-------- .../semmle/code/cpp/security/Encryption.qll | 11 +++ .../CWE-327/BrokenCryptoAlgorithm.expected | 20 ++-- .../query-tests/Security/CWE/CWE-327/test.cpp | 4 +- 4 files changed, 79 insertions(+), 50 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index f1195a43736..1b72331a6cc 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -13,52 +13,72 @@ import cpp import semmle.code.cpp.security.Encryption -abstract class InsecureCryptoSpec extends Locatable { - abstract string description(); -} - -Function getAnInsecureFunction() { - isInsecureEncryption(result.getName()) and +/** + * A function which may relate to an insecure encryption algorithm. + */ +Function getAnInsecureEncryptionFunction() { + ( + isInsecureEncryption(result.getName()) or + isInsecureEncryption(result.getAParameter().getName()) + ) and exists(result.getACallToThisFunction()) } -class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { - InsecureFunctionCall() { - // the function name suggests it relates to an insecure crypto algorithm. - this.getTarget() = getAnInsecureFunction() - } - - override string description() { result = "function call" } - - override string toString() { result = FunctionCall.super.toString() } - - override Location getLocation() { result = FunctionCall.super.getLocation() } +/** + * A function with additional evidence it is related to encryption. + */ +Function getAdditionalEvidenceFunction() { + ( + isEncryptionAdditionalEvidence(result.getName()) or + isEncryptionAdditionalEvidence(result.getAParameter().getName()) + ) and + exists(result.getACallToThisFunction()) } -Macro getAnInsecureMacro() { +/** + * A macro which may relate to an insecure encryption algorithm. + */ +Macro getAnInsecureEncryptionMacro() { isInsecureEncryption(result.getName()) and exists(result.getAnInvocation()) } -class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { - InsecureMacroSpec() { - // the macro name suggests it relates to an insecure crypto algorithm. - this.getMacro() = getAnInsecureMacro() and - // the macro invocation generates something. - exists(this.getAGeneratedElement().(ControlFlowNode)) and - // exclude expressions controlling ifs/switches (as they may not be used). - not any(IfStmt c).getCondition().getAChild*() = this.getAGeneratedElement() and - not any(SwitchCase c).getExpr().getAChild*() = this.getAGeneratedElement() and - // exclude expressions in array initializers (as they may not be used). - not any(AggregateLiteral i).getAChild*() = this.getAGeneratedElement() - } - - override string description() { result = "macro invocation" } - - override string toString() { result = MacroInvocation.super.toString() } - - override Location getLocation() { result = MacroInvocation.super.getLocation() } +/** + * A macro with additional evidence it is related to encryption. + */ +Macro getAdditionalEvidenceMacro() { + isEncryptionAdditionalEvidence(result.getName()) and + exists(result.getAnInvocation()) } -from InsecureCryptoSpec c +/** + * A function call we have a high confidence is related to use of an insecure + * encryption algorithm. + */ +class InsecureFunctionCall extends FunctionCall { + InsecureFunctionCall() { + // find use of an insecure algorithm name + ( + getTarget() = getAnInsecureEncryptionFunction() + or + exists(MacroInvocation mi | + mi.getAGeneratedElement() = this.getAChild*() and + mi.getMacro() = getAnInsecureEncryptionMacro() + ) + ) and + // find additional evidence that this function is related to encryption. + ( + getTarget() = getAdditionalEvidenceFunction() + or + exists(MacroInvocation mi | + mi.getAGeneratedElement() = this.getAChild*() and + mi.getMacro() = getAdditionalEvidenceMacro() + ) + ) + } + + string description() { result = "function call" } +} + +from InsecureFunctionCall c select c, "This " + c.description() + " specifies a broken or weak cryptographic algorithm." diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index d2261df6bc8..692b6ebeae9 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -60,6 +60,17 @@ predicate isInsecureEncryption(string name) { not name.toUpperCase().regexpMatch(".*TRIPLE.*") } + /** + * Holds if there is additional evidence that `name` looks like it might be + * related to operations with an encyption algorithm, besides the name of a + * specific algorithm. This can be used in conjuction with + * `isInsecureEncryption` to produce a stronger heuristic. + */ +bindingset[name] +predicate isEncryptionAdditionalEvidence(string name) { + name.toUpperCase().regexpMatch(".*(CRYPT|CODE|CODING|CBC|KEY).*") +} + /** * Gets a regular expression for matching strings that look like they * contain an algorithm that is known to be secure. diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 53e9054a507..cf31479a533 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,16 +1,14 @@ | test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:62:33:62:40 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:62:2:62:12 | call to encrypt_bad | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:172:28:172:35 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:54:2:54:26 | DES_SET_KEY(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:56:2:56:9 | DES(str) | This macro invocation specifies a broken or weak cryptographic algorithm. | -| test.cpp:59:12:59:25 | SORT_ORDER_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:172:2:172:26 | call to set_encryption_algorithm1 | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:182:2:182:17 | call to encryption_with1 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:38:2:38:31 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:39:2:39:31 | call to my_implementation2 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:51:2:51:32 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:52:2:52:31 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:53:2:53:25 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:54:2:54:26 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index 8d16e5ed98e..ede8f19b761 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -53,10 +53,10 @@ void test_macros(void *data, size_t amount, const char *str) DES_ENCODE(data, amount); // BAD DES_SET_KEY(data, amount); // BAD - DES(str); // GOOD (probably nothing to do with encryption) [FALSE POSITIVE] + DES(str); // GOOD (probably nothing to do with encryption) DESMOND(str); // GOOD (probably nothing to do with encryption) ANODES(str); // GOOD (probably nothing to do with encryption) - int ord = SORT_ORDER_DES; // GOOD (probably nothing to do with encryption) [FALSE POSITIVE] + int ord = SORT_ORDER_DES; // GOOD (probably nothing to do with encryption) } // --- simple encryption function calls --- From 5d1ef49f8feb2fd17ee6adb4dd3ea81c9e9d81e0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 15:42:42 +0100 Subject: [PATCH 356/550] C++: Add support for enum constants. --- .../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 24 +++++++++++++++++++ .../CWE-327/BrokenCryptoAlgorithm.expected | 3 +++ .../Security/CWE/CWE-327/test2.cpp | 6 ++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 1b72331a6cc..b7a34ff8f97 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -51,6 +51,20 @@ Macro getAdditionalEvidenceMacro() { exists(result.getAnInvocation()) } +/** + * An enum constant which may relate to an insecure encryption algorithm. + */ +EnumConstant getAnInsecureEncryptionEnumConst() { + isInsecureEncryption(result.getName()) +} + +/** + * An enum constant with additional evidence it is related to encryption. + */ +EnumConstant getAdditionalEvidenceEnumConst() { + isEncryptionAdditionalEvidence(result.getName()) +} + /** * A function call we have a high confidence is related to use of an insecure * encryption algorithm. @@ -65,6 +79,11 @@ class InsecureFunctionCall extends FunctionCall { mi.getAGeneratedElement() = this.getAChild*() and mi.getMacro() = getAnInsecureEncryptionMacro() ) + or + exists(EnumConstantAccess ec | + ec = this.getAChild*() and + ec.getTarget() = getAnInsecureEncryptionEnumConst() + ) ) and // find additional evidence that this function is related to encryption. ( @@ -74,6 +93,11 @@ class InsecureFunctionCall extends FunctionCall { mi.getAGeneratedElement() = this.getAChild*() and mi.getMacro() = getAdditionalEvidenceMacro() ) + or + exists(EnumConstantAccess ec | + ec = this.getAChild*() and + ec.getTarget() = getAdditionalEvidenceEnumConst() + ) ) } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index cf31479a533..1b85eae2d4c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,8 +1,11 @@ | test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:62:2:62:12 | call to encrypt_bad | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:144:22:144:30 | call to MyBadEncryptor | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:172:2:172:26 | call to set_encryption_algorithm1 | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:175:2:175:26 | call to set_encryption_algorithm2 | This function call specifies a broken or weak cryptographic algorithm. | | test2.cpp:182:2:182:17 | call to encryption_with1 | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:185:2:185:17 | call to encryption_with2 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | call to my_implementation2 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp index 622db3aff0e..def7e0ae808 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -141,7 +141,7 @@ void do_class_encrypts(char *data, size_t amount, keytype key) } { - MyBadEncryptor mbe(key, DES); // BAD [NOT DETECTED] + MyBadEncryptor mbe(key, DES); // BAD mbe.encrypt(data, amount); } @@ -172,7 +172,7 @@ void do_unseen_encrypts(char *data, size_t amount, keytype key) set_encryption_algorithm1(ALGO_DES); // BAD set_encryption_algorithm1(ALGO_AES); // GOOD - set_encryption_algorithm2(USE_DES); // BAD [NOT DETECTED] + set_encryption_algorithm2(USE_DES); // BAD set_encryption_algorithm2(USE_AES); // GOOD set_encryption_algorithm3("DES"); // BAD [NOT DETECTED] @@ -182,7 +182,7 @@ void do_unseen_encrypts(char *data, size_t amount, keytype key) encryption_with1(data, amount, key, ALGO_DES); // BAD encryption_with1(data, amount, key, ALGO_AES); // GOOD - encryption_with2(data, amount, key, USE_DES); // BAD [NOT DETECTED] + encryption_with2(data, amount, key, USE_DES); // BAD encryption_with2(data, amount, key, USE_AES); // GOOD encryption_with3(data, amount, key, "DES"); // BAD [NOT DETECTED] From 2576075b989c05b91b37ef8798052fd944dffb19 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 15:52:28 +0100 Subject: [PATCH 357/550] C++: Repair result message. --- .../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 25 +++++++++++++++---- .../CWE-327/BrokenCryptoAlgorithm.expected | 24 +++++++++--------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index b7a34ff8f97..e96a803d7c0 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -70,19 +70,28 @@ EnumConstant getAdditionalEvidenceEnumConst() { * encryption algorithm. */ class InsecureFunctionCall extends FunctionCall { + Element blame; + string explain; + InsecureFunctionCall() { // find use of an insecure algorithm name ( - getTarget() = getAnInsecureEncryptionFunction() + getTarget() = getAnInsecureEncryptionFunction() and + blame = this and + explain = "function call" or exists(MacroInvocation mi | mi.getAGeneratedElement() = this.getAChild*() and - mi.getMacro() = getAnInsecureEncryptionMacro() + mi.getMacro() = getAnInsecureEncryptionMacro() and + blame = mi and + explain = "macro invocation" ) or exists(EnumConstantAccess ec | ec = this.getAChild*() and - ec.getTarget() = getAnInsecureEncryptionEnumConst() + ec.getTarget() = getAnInsecureEncryptionEnumConst() and + blame = ec and + explain = "enum constant access" ) ) and // find additional evidence that this function is related to encryption. @@ -101,8 +110,14 @@ class InsecureFunctionCall extends FunctionCall { ) } - string description() { result = "function call" } + Element getBlame() { + result = blame + } + + string getDescription() { + result = explain + } } from InsecureFunctionCall c -select c, "This " + c.description() + " specifies a broken or weak cryptographic algorithm." +select c.getBlame(), "This " + c.getDescription() + " specifies a broken or weak cryptographic algorithm." diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 1b85eae2d4c..197472977b4 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,17 +1,17 @@ | test2.cpp:49:4:49:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:62:2:62:12 | call to encrypt_bad | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:62:33:62:40 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:124:4:124:24 | call to my_des_implementation | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:144:22:144:30 | call to MyBadEncryptor | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:172:2:172:26 | call to set_encryption_algorithm1 | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:175:2:175:26 | call to set_encryption_algorithm2 | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:182:2:182:17 | call to encryption_with1 | This function call specifies a broken or weak cryptographic algorithm. | -| test2.cpp:185:2:185:17 | call to encryption_with2 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:38:2:38:31 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:39:2:39:31 | call to my_implementation2 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:51:2:51:32 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:52:2:52:31 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:53:2:53:25 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | -| test.cpp:54:2:54:26 | call to my_implementation1 | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:144:27:144:29 | DES | This enum constant access specifies a broken or weak cryptographic algorithm. | +| test2.cpp:172:28:172:35 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:175:28:175:34 | USE_DES | This enum constant access specifies a broken or weak cryptographic algorithm. | +| test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test2.cpp:185:38:185:44 | USE_DES | This enum constant access specifies a broken or weak cryptographic algorithm. | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:54:2:54:26 | DES_SET_KEY(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | From 3a83ff54e6c95dcbe878d61eee2dca06199cb10b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 16:02:00 +0100 Subject: [PATCH 358/550] C++: Add support for class methods. --- cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 3 ++- .../Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected | 2 ++ cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index e96a803d7c0..71c835491f4 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -19,7 +19,8 @@ import semmle.code.cpp.security.Encryption Function getAnInsecureEncryptionFunction() { ( isInsecureEncryption(result.getName()) or - isInsecureEncryption(result.getAParameter().getName()) + isInsecureEncryption(result.getAParameter().getName()) or + isInsecureEncryption(result.getDeclaringType().getName()) ) and exists(result.getACallToThisFunction()) } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index 197472977b4..e213a6e92c3 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -6,6 +6,8 @@ | test2.cpp:175:28:175:34 | USE_DES | This enum constant access specifies a broken or weak cryptographic algorithm. | | test2.cpp:182:38:182:45 | ALGO_DES | This macro invocation specifies a broken or weak cryptographic algorithm. | | test2.cpp:185:38:185:44 | USE_DES | This enum constant access specifies a broken or weak cryptographic algorithm. | +| test2.cpp:234:2:234:20 | call to encrypt | This function call specifies a broken or weak cryptographic algorithm. | +| test2.cpp:239:5:239:11 | call to encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp index def7e0ae808..1cf18659242 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -231,12 +231,12 @@ public: void do_classes(const char *data) { - desEncrypt::encrypt(data); // BAD [NOT DETECTED] + desEncrypt::encrypt(data); // BAD aes256Encrypt::encrypt(data); // GOOD desCipher dc; aesCipher ac; - dc.encrypt(data); // BAD [NOT DETECTED] + dc.encrypt(data); // BAD ac.encrypt(data); // GOOD } From a9d57450c89bb93b0882e9c723ebabc5684c0c52 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 16:19:09 +0100 Subject: [PATCH 359/550] C++: Autoformat. --- .../CWE/CWE-327/BrokenCryptoAlgorithm.ql | 19 ++++++------------- .../semmle/code/cpp/security/Encryption.qll | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 71c835491f4..2f1b7fed6f0 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -55,16 +55,12 @@ Macro getAdditionalEvidenceMacro() { /** * An enum constant which may relate to an insecure encryption algorithm. */ -EnumConstant getAnInsecureEncryptionEnumConst() { - isInsecureEncryption(result.getName()) -} +EnumConstant getAnInsecureEncryptionEnumConst() { isInsecureEncryption(result.getName()) } /** * An enum constant with additional evidence it is related to encryption. */ -EnumConstant getAdditionalEvidenceEnumConst() { - isEncryptionAdditionalEvidence(result.getName()) -} +EnumConstant getAdditionalEvidenceEnumConst() { isEncryptionAdditionalEvidence(result.getName()) } /** * A function call we have a high confidence is related to use of an insecure @@ -111,14 +107,11 @@ class InsecureFunctionCall extends FunctionCall { ) } - Element getBlame() { - result = blame - } + Element getBlame() { result = blame } - string getDescription() { - result = explain - } + string getDescription() { result = explain } } from InsecureFunctionCall c -select c.getBlame(), "This " + c.getDescription() + " specifies a broken or weak cryptographic algorithm." +select c.getBlame(), + "This " + c.getDescription() + " specifies a broken or weak cryptographic algorithm." diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 692b6ebeae9..8c9c4b3beee 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -60,7 +60,7 @@ predicate isInsecureEncryption(string name) { not name.toUpperCase().regexpMatch(".*TRIPLE.*") } - /** +/** * Holds if there is additional evidence that `name` looks like it might be * related to operations with an encyption algorithm, besides the name of a * specific algorithm. This can be used in conjuction with From 9cdf8389815e4bb97e3ed3dd18ae3820fadb17c9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 13 May 2021 16:20:52 +0100 Subject: [PATCH 360/550] C++: Bug fix. --- cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 2f1b7fed6f0..0d311333fca 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -31,7 +31,8 @@ Function getAnInsecureEncryptionFunction() { Function getAdditionalEvidenceFunction() { ( isEncryptionAdditionalEvidence(result.getName()) or - isEncryptionAdditionalEvidence(result.getAParameter().getName()) + isEncryptionAdditionalEvidence(result.getAParameter().getName()) or + isInsecureEncryption(result.getDeclaringType().getName()) ) and exists(result.getACallToThisFunction()) } From 51067af784f47fccb040a6d99ac16491b6a58cf6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 22:34:10 +0200 Subject: [PATCH 361/550] add "uid" (and friends) as maybe being sensitive account info --- .../internal/SensitiveDataHeuristics.qll | 3 +- .../CWE-338/InsecureRandomness.expected | 40 +++++++++++++++++++ .../test/query-tests/Security/CWE-338/tst.js | 9 ++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..9a3a306c159 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -58,7 +58,8 @@ module HeuristicNames { */ string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or - result = "(?is).*(puid|username|userid).*" + result = "(?is).*(puid|username|userid).*" or + result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected index e4ab385cc07..4db1bb3b088 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected +++ b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected @@ -66,6 +66,26 @@ nodes | tst.js:95:33:95:45 | Math.random() | | tst.js:95:33:95:45 | Math.random() | | tst.js:95:33:95:45 | Math.random() | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:27:115:39 | Math.random() | +| tst.js:115:27:115:39 | Math.random() | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:33:116:45 | Math.random() | +| tst.js:116:33:116:45 | Math.random() | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:26:117:38 | Math.random() | +| tst.js:117:26:117:38 | Math.random() | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:34:118:46 | Math.random() | +| tst.js:118:34:118:46 | Math.random() | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | edges | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | @@ -114,6 +134,22 @@ edges | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | +| tst.js:115:27:115:39 | Math.random() | tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:115:27:115:39 | Math.random() | tst.js:115:27:115:55 | Math.ra ... 000_000 | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:115:27:115:55 | Math.ra ... 000_000 | tst.js:115:16:115:56 | Math.fl ... 00_000) | +| tst.js:116:33:116:45 | Math.random() | tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:116:33:116:45 | Math.random() | tst.js:116:33:116:61 | Math.ra ... 000_000 | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:116:33:116:61 | Math.ra ... 000_000 | tst.js:116:22:116:62 | Math.fl ... 00_000) | +| tst.js:117:26:117:38 | Math.random() | tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:117:26:117:38 | Math.random() | tst.js:117:26:117:54 | Math.ra ... 000_000 | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:117:26:117:54 | Math.ra ... 000_000 | tst.js:117:15:117:55 | Math.fl ... 00_000) | +| tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | #select | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:2:20:2:32 | Math.random() | random value | | tst.js:6:20:6:43 | "prefix ... andom() | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | Cryptographically insecure $@ in a security context. | tst.js:6:31:6:43 | Math.random() | random value | @@ -131,3 +167,7 @@ edges | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | tst.js:84:19:84:31 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:84:19:84:31 | Math.random() | random value | | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | tst.js:90:32:90:44 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:90:32:90:44 | Math.random() | random value | | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | tst.js:95:33:95:45 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:95:33:95:45 | Math.random() | random value | +| tst.js:115:16:115:56 | Math.fl ... 00_000) | tst.js:115:27:115:39 | Math.random() | tst.js:115:16:115:56 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:115:27:115:39 | Math.random() | random value | +| tst.js:116:22:116:62 | Math.fl ... 00_000) | tst.js:116:33:116:45 | Math.random() | tst.js:116:22:116:62 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:116:33:116:45 | Math.random() | random value | +| tst.js:117:15:117:55 | Math.fl ... 00_000) | tst.js:117:26:117:38 | Math.random() | tst.js:117:15:117:55 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:117:26:117:38 | Math.random() | random value | +| tst.js:118:23:118:63 | Math.fl ... 00_000) | tst.js:118:34:118:46 | Math.random() | tst.js:118:23:118:63 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:118:34:118:46 | Math.random() | random value | diff --git a/javascript/ql/test/query-tests/Security/CWE-338/tst.js b/javascript/ql/test/query-tests/Security/CWE-338/tst.js index 123799426b5..6a1abf1403c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-338/tst.js @@ -109,4 +109,11 @@ function f18() { } }; var secret = genRandom(); // OK - Math.random() is only a fallback. -})(); \ No newline at end of file +})(); + +function uid() { + var uuid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var sessionUid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var my_nice_uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK +} \ No newline at end of file From 662e335424a7205100388feaa263db724a850191 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 22:54:39 +0200 Subject: [PATCH 362/550] keep python in sync --- .../python/security/internal/SensitiveDataHeuristics.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index ddf95b1b534..9a3a306c159 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -58,7 +58,8 @@ module HeuristicNames { */ string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or - result = "(?is).*(puid|username|userid).*" + result = "(?is).*(puid|username|userid).*" or + result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" } /** From 9d60ec035f991655e7ea4ead67552ec567df64c2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 13 May 2021 23:04:30 +0200 Subject: [PATCH 363/550] fix casing on the uid regexp --- .../security/internal/SensitiveDataHeuristics.qll | 2 +- .../Security/CWE-338/InsecureRandomness.expected | 10 ++++++++++ javascript/ql/test/query-tests/Security/CWE-338/tst.js | 3 +++ .../security/internal/SensitiveDataHeuristics.qll | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index 9a3a306c159..589c37120b9 100644 --- a/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -59,7 +59,7 @@ module HeuristicNames { string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or result = "(?is).*(puid|username|userid).*" or - result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" + result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected index 4db1bb3b088..42da210c266 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected +++ b/javascript/ql/test/query-tests/Security/CWE-338/InsecureRandomness.expected @@ -86,6 +86,12 @@ nodes | tst.js:118:34:118:46 | Math.random() | | tst.js:118:34:118:46 | Math.random() | | tst.js:118:34:118:62 | Math.ra ... 000_000 | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:120:16:120:28 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | edges | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | @@ -150,6 +156,8 @@ edges | tst.js:118:34:118:46 | Math.random() | tst.js:118:34:118:62 | Math.ra ... 000_000 | | tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | | tst.js:118:34:118:62 | Math.ra ... 000_000 | tst.js:118:23:118:63 | Math.fl ... 00_000) | +| tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | +| tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | #select | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | tst.js:2:20:2:32 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:2:20:2:32 | Math.random() | random value | | tst.js:6:20:6:43 | "prefix ... andom() | tst.js:6:31:6:43 | Math.random() | tst.js:6:20:6:43 | "prefix ... andom() | Cryptographically insecure $@ in a security context. | tst.js:6:31:6:43 | Math.random() | random value | @@ -171,3 +179,5 @@ edges | tst.js:116:22:116:62 | Math.fl ... 00_000) | tst.js:116:33:116:45 | Math.random() | tst.js:116:22:116:62 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:116:33:116:45 | Math.random() | random value | | tst.js:117:15:117:55 | Math.fl ... 00_000) | tst.js:117:26:117:38 | Math.random() | tst.js:117:15:117:55 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:117:26:117:38 | Math.random() | random value | | tst.js:118:23:118:63 | Math.fl ... 00_000) | tst.js:118:34:118:46 | Math.random() | tst.js:118:23:118:63 | Math.fl ... 00_000) | Cryptographically insecure $@ in a security context. | tst.js:118:34:118:46 | Math.random() | random value | +| tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | tst.js:120:16:120:28 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:120:16:120:28 | Math.random() | random value | +| tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | tst.js:121:18:121:30 | Math.random() | Cryptographically insecure $@ in a security context. | tst.js:121:18:121:30 | Math.random() | random value | diff --git a/javascript/ql/test/query-tests/Security/CWE-338/tst.js b/javascript/ql/test/query-tests/Security/CWE-338/tst.js index 6a1abf1403c..77393b8983c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-338/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-338/tst.js @@ -116,4 +116,7 @@ function uid() { var sessionUid = Math.floor(Math.random() * 4_000_000_000); // NOT OK var uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK var my_nice_uid = Math.floor(Math.random() * 4_000_000_000); // NOT OK + var liquid = Math.random(); // OK + var UUID = Math.random(); // NOT OK + var MY_UID = Math.random(); // NOK OK } \ No newline at end of file diff --git a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll index 9a3a306c159..589c37120b9 100644 --- a/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll +++ b/python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll @@ -59,7 +59,7 @@ module HeuristicNames { string maybeAccountInfo() { result = "(?is).*acc(ou)?nt.*" or result = "(?is).*(puid|username|userid).*" or - result = "(?is).*(u|^|_|[a-z(?=U)])(uid).*" + result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*" } /** From 406fb1e383dcf3408b43fc752bf608bdc3b68fda Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Thu, 13 May 2021 17:29:34 -0400 Subject: [PATCH 364/550] Update with Go custom build options --- .../codeql-cli/creating-codeql-databases.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 4f7212050df..a41727e1956 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -165,13 +165,14 @@ build steps, you may need to explicitly define each step in the command line. .. pull-quote:: Creating databases for Go - For Go, you should always use the CodeQL autobuilder. Install the Go - toolchain (version 1.11 or later) and, if there are dependencies, the - appropriate dependency manager (such as `dep + For Go, install the Go toolchain (version 1.11 or later) and, if there + are dependencies, the appropriate dependency manager (such as `dep `__). - Do not specify any build commands, as you will override the autobuilder - invocation, which will create an empty database. + The Go autobuilder attempts to automatically detect Go code in a repository, + and only runs build scripts in an attempt to fetch dependencies. To force + CodeQL to use your build script, set the environment variable + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or pass a command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -200,6 +201,10 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' +- Go project built using a custom build script:: + + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go --command='./scripts/build.sh' + - Java project built using Gradle:: codeql database create java-database --language=java --command='gradle clean test' From 498c99e26ca556e466f00015a6203f3ce64a5362 Mon Sep 17 00:00:00 2001 From: haby0 Date: Fri, 14 May 2021 16:31:59 +0800 Subject: [PATCH 365/550] Add left value, Add return expression tracing flow --- .../CWE/CWE-601/SpringUrlRedirect.qll | 70 +++++++++++++++++-- .../CWE-601/SpringUrlRedirect.expected | 8 +++ .../security/CWE-601/SpringUrlRedirect.java | 18 ++++- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 1ab5f3cd0b1..866eaae1c34 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -18,20 +18,48 @@ class StartsWithSanitizer extends DataFlow::BarrierGuard { } /** - * A concatenate expression using the string `redirect:` on the left. + * A concatenate expression using the string `redirect:` or `ajaxredirect:` or `forward:` on the left. * * E.g: `"redirect:" + redirectUrl` */ class RedirectBuilderExpr extends AddExpr { RedirectBuilderExpr() { - this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() = "redirect:" + this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() in [ + "redirect:", "ajaxredirect:", "forward:" + ] + } +} + +/** + * A call to `StringBuilder.append` or `StringBuffer.append` method, and the parameter value is + * `"redirect:"` or `"ajaxredirect:"` or `"forward:"`. + * + * E.g: `StringBuilder.append("redirect:")` + */ +class RedirectAppendCall extends MethodAccess { + RedirectAppendCall() { + this.getMethod().hasName("append") and + this.getMethod().getDeclaringType() instanceof StringBuildingType and + this.getArgument(0).(CompileTimeConstantExpr).getStringValue() in [ + "redirect:", "ajaxredirect:", "forward:" + ] } } /** A URL redirection sink from spring controller method. */ class SpringUrlRedirectSink extends DataFlow::Node { SpringUrlRedirectSink() { - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + exists(RedirectBuilderExpr rbe | + rbe.getRightOperand() = this.asExpr() and + exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(rbe), _)) + ) + or + exists(MethodAccess ma, RedirectAppendCall rac | + DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and + ma.getMethod().hasName("append") and + ma.getArgument(0) = this.asExpr() and + exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(ma.getQualifier()), _)) + ) or exists(MethodAccess ma | ma.getMethod().hasName("setUrl") and @@ -50,8 +78,40 @@ class SpringUrlRedirectSink extends DataFlow::Node { or exists(ClassInstanceExpr cie | cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and - cie.getArgument(0) = this.asExpr() and - exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr()) + exists(RedirectBuilderExpr rbe | + rbe = cie.getArgument(0) and rbe.getRightOperand() = this.asExpr() + ) + ) + } +} + +/** A data flow configuration tracing flow from redirect builder expression to spring controller method return expression. */ +private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { + RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } + + override predicate isSource(DataFlow::Node src) { + exists(RedirectBuilderExpr rbe | rbe = src.asExpr()) + or + exists(MethodAccess ma, RedirectAppendCall rac | + DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and + ma.getMethod().hasName("append") and + ma.getQualifier() = src.asExpr() + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(ReturnStmt rs, SpringRequestMappingMethod sqmm | + rs.getResult() = sink.asExpr() and + sqmm.getBody().getAStmt() = rs + ) + } + + override predicate isAdditionalFlowStep(Node prod, Node succ) { + exists(MethodAccess ma | + ma.getMethod().hasName("toString") and + ma.getMethod().getDeclaringType() instanceof StringBuildingType and + ma.getQualifier() = prod.asExpr() and + ma = succ.asExpr() ) } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected index fee0598bbee..26b8acd7770 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -3,6 +3,8 @@ edges | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | +| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | +| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | nodes | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | @@ -12,8 +14,14 @@ nodes | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl | | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl | #select | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java index 1438b0a63a1..5124d8cd8c6 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -34,6 +34,22 @@ public class SpringUrlRedirect { } @GetMapping("url5") + public String bad5(String redirectUrl) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("redirect:"); + stringBuffer.append(redirectUrl); + return stringBuffer.toString(); + } + + @GetMapping("url6") + public String bad6(String redirectUrl) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("redirect:"); + stringBuilder.append(redirectUrl); + return stringBuilder.toString(); + } + + @GetMapping("url7") public RedirectView good1(String redirectUrl) { RedirectView rv = new RedirectView(); if (redirectUrl.startsWith(VALID_REDIRECT)){ @@ -44,7 +60,7 @@ public class SpringUrlRedirect { return rv; } - @GetMapping("url6") + @GetMapping("url8") public ModelAndView good2(String token) { String url = "/edit?token=" + token; return new ModelAndView("redirect:" + url); From f378513ea39f33b15879653e489653d719633910 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Fri, 14 May 2021 11:19:20 +0100 Subject: [PATCH 366/550] Add lines-of-code tags This is a proposed method for advertising which queries are measuring the lines of code in a project in a more robust manner than inspecting the rule id. Note that the python "LinesOfUserCode" query should _not_ have this property, as otherwise the results of the two queries will be summed. --- cpp/ql/src/Summary/LinesOfCode.ql | 1 + csharp/ql/src/Metrics/Summaries/LinesOfCode.ql | 1 + java/ql/src/Metrics/Summaries/LinesOfCode.ql | 1 + javascript/ql/src/Summary/LinesOfCode.ql | 1 + python/ql/src/Summary/LinesOfCode.ql | 1 + 5 files changed, 5 insertions(+) diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql index 3b2aa2ac4c9..2d816b349e8 100644 --- a/cpp/ql/src/Summary/LinesOfCode.ql +++ b/cpp/ql/src/Summary/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import cpp diff --git a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql index 9156a5e4a7f..e93e3c7416f 100644 --- a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql +++ b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of code across all files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import csharp diff --git a/java/ql/src/Metrics/Summaries/LinesOfCode.ql b/java/ql/src/Metrics/Summaries/LinesOfCode.ql index d6db7c6ee6b..c622f8b08ba 100644 --- a/java/ql/src/Metrics/Summaries/LinesOfCode.ql +++ b/java/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -6,6 +6,7 @@ * or comments. * @kind metric * @tags summary + * lines-of-code */ import java diff --git a/javascript/ql/src/Summary/LinesOfCode.ql b/javascript/ql/src/Summary/LinesOfCode.ql index 9f89e0e2163..cadf0a9cf8f 100644 --- a/javascript/ql/src/Summary/LinesOfCode.ql +++ b/javascript/ql/src/Summary/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import javascript diff --git a/python/ql/src/Summary/LinesOfCode.ql b/python/ql/src/Summary/LinesOfCode.ql index d9bfc4f872c..ad0b77730de 100644 --- a/python/ql/src/Summary/LinesOfCode.ql +++ b/python/ql/src/Summary/LinesOfCode.ql @@ -5,6 +5,7 @@ * database. This query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code * @id py/summary/lines-of-code */ From 5031b73f3509e4d1c1ce21586dd1262502177670 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:43:20 +0200 Subject: [PATCH 367/550] C++: Add barrier to cpp/uncontrolled-allocation-size that blocks flow when overflow isn't possible. --- .../CWE/CWE-190/TaintedAllocationSize.ql | 16 ++++++++++ .../TaintedAllocationSize.expected | 30 ------------------- .../semmle/TaintedAllocationSize/test.cpp | 6 ++-- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index cc2d52385c7..859d56ec2a0 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -12,6 +12,7 @@ */ import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.security.TaintTracking import TaintedWithPath @@ -27,6 +28,21 @@ predicate allocSink(Expr alloc, Expr tainted) { class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration { override predicate isSink(Element tainted) { allocSink(_, tainted) } + + override predicate isBarrier(Expr e) { + super.isBarrier(e) + or + // There can be two separate reasons for `convertedExprMightOverflow` not holding: + // 1. `e` really cannot overflow. + // 2. `e` isn't analyzable. + // If we didn't rule out case 2 we would place barriers on anything that isn't analyzable. + ( + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation + ) and + not convertedExprMightOverflow(e) + } } predicate taintedAllocSize( diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 25ff3162973..cdcf3aa6847 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -53,10 +53,6 @@ edges | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | | test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | | test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | | test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | | test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | | test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store | @@ -91,14 +87,6 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -149,11 +137,6 @@ nodes | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | | test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:138:19:138:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | | test.cpp:201:9:201:42 | Store | semmle.label | Store | | test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv | | test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... | @@ -196,16 +179,6 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:301:19:301:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:309:19:309:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -216,7 +189,6 @@ nodes | test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) | | test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | | test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | -| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) | | test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | @@ -224,5 +196,3 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:305:4:305:9 | call to malloc | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:301:19:301:24 | call to getenv | user input (getenv) | -| test.cpp:314:3:314:8 | call to malloc | test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:309:19:309:24 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 943bc3b1214..d00dc10a445 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -139,7 +139,7 @@ void more_bounded_tests() { if (size > 0) { - malloc(size * sizeof(int)); // BAD + malloc(size * sizeof(int)); // GOOD (overflow not possible) } } @@ -302,7 +302,7 @@ void equality_cases() { if ((size == 50) || (size == 100)) { - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } { @@ -311,6 +311,6 @@ void equality_cases() { if (size != 50 && size != 100) return; - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } From 1497fba6f249c757d0a37d832c49d712e879af34 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 14 May 2021 11:43:49 +0000 Subject: [PATCH 368/550] Remove the isAdditionalTaintStep predicate --- .../Security/CWE/CWE-094/JythonInjection.ql | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 9991c0901dc..0f23edc69f4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -102,16 +102,6 @@ class CodeInjectionConfiguration extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - // @RequestBody MyQueryObj query; interpreter.exec(query.getInterpreterCode()); - exists(MethodAccess ma | - ma.getMethod().getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and - not ma.getMethod().getDeclaringType() instanceof TypeObject and - ma.getQualifier() = node1.asExpr() and - ma = node2.asExpr() - ) - } } from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf From c1d41b31695edc664e2b3d8b1ca10cac3dda721b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:47:23 +0200 Subject: [PATCH 369/550] C++: Add false positive result from pointer-difference expressions. --- .../TaintedAllocationSize.expected | 14 ++++++++++++++ .../CWE-190/semmle/TaintedAllocationSize/test.cpp | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index cdcf3aa6847..a96865cae60 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -87,6 +87,12 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | +| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -179,6 +185,13 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | +| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | +| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | +| test.cpp:324:9:324:14 | offset | semmle.label | offset | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -196,3 +209,4 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:324:2:324:7 | call to malloc | test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | This allocation size is derived from $@ and might overflow | test.cpp:321:15:321:20 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index d00dc10a445..0d630ac99cb 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -314,3 +314,12 @@ void equality_cases() { malloc(size * sizeof(int)); // GOOD } } + +char * strstr(char *, const char *); + +void ptr_diff_case() { + char* user = getenv("USER"); + char* admin_begin_pos = strstr(user, "ADMIN"); + int offset = admin_begin_pos ? user - admin_begin_pos : 0; + malloc(offset); // GOOD [FALSE POSITIVE] +} \ No newline at end of file From 2d0a56128d8f10acade04f99c73493671186d298 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 13:49:48 +0200 Subject: [PATCH 370/550] C++: Prevent flow out of pointer-difference expressions. --- .../CWE/CWE-190/TaintedAllocationSize.ql | 6 +++ .../TaintedAllocationSize.expected | 48 ------------------- .../semmle/TaintedAllocationSize/test.cpp | 4 +- 3 files changed, 8 insertions(+), 50 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index 859d56ec2a0..75cac365a1a 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -42,6 +42,12 @@ class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration { e instanceof AssignArithmeticOperation ) and not convertedExprMightOverflow(e) + or + // Subtracting two pointers is either well-defined (and the result will likely be small), or + // terribly undefined and dangerous. Here, we assume that the programmer has ensured that the + // result is well-defined (i.e., the two pointers point to the same object), and thus the result + // will likely be small. + e = any(PointerDiffExpr diff).getAnOperand() } } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index a96865cae60..234efe2c35b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -27,24 +27,6 @@ edges | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:100:4:100:15 | buffer | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | buffer indirection | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:101:4:101:15 | ... + ... | test.cpp:75:38:75:40 | end | -| test.cpp:101:4:101:15 | buffer | test.cpp:75:25:75:29 | start | | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | | test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | @@ -87,12 +69,6 @@ edges | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | | test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | (size_t)... | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | -| test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | nodes | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | @@ -118,21 +94,6 @@ nodes | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | | test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | buffer | semmle.label | buffer | -| test.cpp:75:25:75:29 | start | semmle.label | start | -| test.cpp:75:38:75:40 | end | semmle.label | end | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:97:18:97:23 | buffer | semmle.label | buffer | -| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument | -| test.cpp:100:4:100:15 | buffer | semmle.label | buffer | -| test.cpp:100:17:100:22 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument | -| test.cpp:101:4:101:15 | ... + ... | semmle.label | ... + ... | -| test.cpp:101:4:101:15 | buffer | semmle.label | buffer | | test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv | | test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... | | test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | @@ -185,13 +146,6 @@ nodes | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | | test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | -| test.cpp:321:15:321:20 | call to getenv | semmle.label | call to getenv | -| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:324:9:324:14 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | -| test.cpp:324:9:324:14 | offset | semmle.label | offset | #select | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -199,7 +153,6 @@ nodes | test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) | | test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | | test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | | test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | @@ -209,4 +162,3 @@ nodes | test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | | test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | | test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:324:2:324:7 | call to malloc | test.cpp:321:15:321:20 | call to getenv | test.cpp:324:9:324:14 | offset | This allocation size is derived from $@ and might overflow | test.cpp:321:15:321:20 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 0d630ac99cb..57eb5b91a07 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -76,7 +76,7 @@ void processData2(char *start, char *end) { char *copy; - copy = new char[end - start]; // GOOD [FALSE POSITIVE] + copy = new char[end - start]; // GOOD // ... @@ -321,5 +321,5 @@ void ptr_diff_case() { char* user = getenv("USER"); char* admin_begin_pos = strstr(user, "ADMIN"); int offset = admin_begin_pos ? user - admin_begin_pos : 0; - malloc(offset); // GOOD [FALSE POSITIVE] + malloc(offset); // GOOD } \ No newline at end of file From 58dde68b10d74d6d7ed4b7d206c1fe319b4e15ac Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 14 May 2021 14:16:00 +0200 Subject: [PATCH 371/550] C++: Add change-note. --- cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md diff --git a/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md new file mode 100644 index 00000000000..6f0c9d6fa98 --- /dev/null +++ b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md @@ -0,0 +1,2 @@ +lgtm +* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives. From 4cf695b5ab0a8a50e5143f298e8e1ff3e7ec3c30 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 10:00:17 -0400 Subject: [PATCH 372/550] specify ``--command`` option Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index a41727e1956..8810e3deb9d 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -172,7 +172,7 @@ build steps, you may need to explicitly define each step in the command line. The Go autobuilder attempts to automatically detect Go code in a repository, and only runs build scripts in an attempt to fetch dependencies. To force CodeQL to use your build script, set the environment variable - `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or pass a command. + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ From 6dd30ee5e2a03fdffc8b7cb8bf23689bce2813b7 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:00:33 -0400 Subject: [PATCH 373/550] clarify options for tracing Co-authored-by: Chris Smowton --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 8810e3deb9d..9cdd02f78fc 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -171,8 +171,9 @@ build steps, you may need to explicitly define each step in the command line. The Go autobuilder attempts to automatically detect Go code in a repository, and only runs build scripts in an attempt to fetch dependencies. To force - CodeQL to use your build script, set the environment variable - `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option. + CodeQL to limit extraction to the files compiled by your build script, set the environment variable + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option to specify a + build command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ From 0e99d5e379041cdffcaab2da4e57218648d1fd96 Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:05:55 -0400 Subject: [PATCH 374/550] Add examples of both tracing mechanisms --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 9cdd02f78fc..8658da9a1d4 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -202,9 +202,12 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' +- Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable:: + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go + - Go project built using a custom build script:: - CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go --command='./scripts/build.sh' + codeql database create go-database --language=go --command='./scripts/build.sh' - Java project built using Gradle:: From 58c746e42b174c1ee4465569e832c7a7cb735d03 Mon Sep 17 00:00:00 2001 From: Ethan P <56270045+ethanpalm@users.noreply.github.com> Date: Fri, 14 May 2021 14:09:07 -0400 Subject: [PATCH 375/550] fix formatting --- docs/codeql/codeql-cli/creating-codeql-databases.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 8658da9a1d4..1552a077e24 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -203,6 +203,7 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' - Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable:: + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go - Go project built using a custom build script:: From 73c7e15580bc9591842dcfc4fa872def88a74ecc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 14 May 2021 22:25:00 +0200 Subject: [PATCH 376/550] Java: Add back StringInputStream to CloseReader.ql --- java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql index 73a9c81f343..e93c59b0879 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql @@ -25,6 +25,9 @@ predicate readerType(RefType t) { predicate safeReaderType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"]) + or + // Note: It is unclear which specific class this is supposed to match + sup.hasName("StringInputStream") ) } From e205e4bbcee12e121669e661f288d4fc6806e89c Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Fri, 14 May 2021 22:31:14 +0200 Subject: [PATCH 377/550] Java: Add change note for close resource query changes --- .../2021-05-14-close-resource-leaks-improvements.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-05-14-close-resource-leaks-improvements.md diff --git a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md new file mode 100644 index 00000000000..bae8640d85a --- /dev/null +++ b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries have been made more specific to only consider JDK classes and subtypes. Additionally the number of false positives has been reduced. From 31091c66c19fdb401967decc596621fa68db8d0a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 17 May 2021 08:06:06 +0200 Subject: [PATCH 378/550] C++: Add a test containing a guarded long. --- .../TaintedAllocationSize.expected | 332 +++++++++--------- .../semmle/TaintedAllocationSize/test.cpp | 10 + 2 files changed, 181 insertions(+), 161 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 234efe2c35b..752e9165c07 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -1,164 +1,174 @@ edges -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store | -| test.cpp:201:14:201:27 | (const char *)... | test.cpp:201:9:201:42 | Store | -| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | -| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | -| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | -| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | (size_t)... | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:235:2:235:9 | local_size | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:237:2:237:8 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | (size_t)... | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:235:2:235:9 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:2:237:8 | local_size | -| test.cpp:235:2:235:9 | local_size | test.cpp:214:23:214:23 | s | -| test.cpp:237:2:237:8 | local_size | test.cpp:220:21:220:21 | s | -| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:279:17:279:20 | get_size output argument [array content] | -| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:295:18:295:21 | get_size output argument [array content] | -| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi [array content] | -| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi [array content] | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument [array content] | test.cpp:279:17:279:20 | Chi | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:14:211:19 | call to getenv | test.cpp:211:9:211:42 | Store | +| test.cpp:211:14:211:27 | (const char *)... | test.cpp:211:9:211:42 | Store | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:247:2:247:8 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:247:2:247:8 | local_size | +| test.cpp:245:2:245:9 | local_size | test.cpp:224:23:224:23 | s | +| test.cpp:247:2:247:8 | local_size | test.cpp:230:21:230:21 | s | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:289:17:289:20 | get_size output argument [array content] | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:305:18:305:21 | get_size output argument [array content] | +| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | get_size output argument [array content] | test.cpp:289:17:289:20 | Chi | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | get_size output argument [array content] | test.cpp:305:18:305:21 | Chi | nodes -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv | -| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:132:19:132:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:201:9:201:42 | Store | semmle.label | Store | -| test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv | -| test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:214:23:214:23 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:220:21:220:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:227:24:227:29 | call to getenv | semmle.label | call to getenv | -| test.cpp:227:24:227:37 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:235:2:235:9 | local_size | semmle.label | local_size | -| test.cpp:237:2:237:8 | local_size | semmle.label | local_size | -| test.cpp:241:2:241:32 | Chi [array content] | semmle.label | Chi [array content] | -| test.cpp:241:2:241:32 | ChiPartial | semmle.label | ChiPartial | -| test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv | -| test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv | -| test.cpp:249:20:249:33 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:279:17:279:20 | Chi | semmle.label | Chi | -| test.cpp:279:17:279:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:295:18:295:21 | Chi | semmle.label | Chi | -| test.cpp:295:18:295:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:124:18:124:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | semmle.label | call to getenv | +| test.cpp:133:19:133:32 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:148:20:148:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:211:9:211:42 | Store | semmle.label | Store | +| test.cpp:211:14:211:19 | call to getenv | semmle.label | call to getenv | +| test.cpp:211:14:211:27 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:224:23:224:23 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:230:21:230:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:237:24:237:29 | call to getenv | semmle.label | call to getenv | +| test.cpp:237:24:237:37 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:245:2:245:9 | local_size | semmle.label | local_size | +| test.cpp:247:2:247:8 | local_size | semmle.label | local_size | +| test.cpp:251:2:251:32 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:251:2:251:32 | ChiPartial | semmle.label | ChiPartial | +| test.cpp:251:18:251:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:251:18:251:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:259:20:259:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:259:20:259:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:289:17:289:20 | Chi | semmle.label | Chi | +| test.cpp:289:17:289:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:305:18:305:21 | Chi | semmle.label | Chi | +| test.cpp:305:18:305:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | #select -| test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:45:31:45:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | -| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | -| test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:231:2:231:7 | call to malloc | test.cpp:201:14:201:19 | call to getenv | test.cpp:231:9:231:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:201:14:201:19 | call to getenv | user input (getenv) | -| test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | -| test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:43:31:43:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:44:31:44:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:46:31:46:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:49:25:49:30 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:50:17:50:30 | new[] | test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:53:21:53:27 | call to realloc | test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:128:17:128:22 | call to malloc | test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:124:18:124:23 | call to getenv | user input (getenv) | +| test.cpp:135:3:135:8 | call to malloc | test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:133:19:133:24 | call to getenv | user input (getenv) | +| test.cpp:152:4:152:9 | call to malloc | test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:148:20:148:25 | call to getenv | user input (getenv) | +| test.cpp:225:14:225:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:225:21:225:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:231:14:231:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:231:21:231:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:239:2:239:7 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:241:2:241:7 | call to malloc | test.cpp:211:14:211:19 | call to getenv | test.cpp:241:9:241:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:211:14:211:19 | call to getenv | user input (getenv) | +| test.cpp:263:4:263:9 | call to malloc | test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:259:20:259:25 | call to getenv | user input (getenv) | +| test.cpp:291:4:291:9 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:291:11:291:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | +| test.cpp:308:3:308:8 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:308:10:308:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 57eb5b91a07..b11a136ed24 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -7,6 +7,7 @@ void *malloc(size_t size); void *realloc(void *ptr, size_t size); void free(void *ptr); int atoi(const char *nptr); +long atol(const char *nptr); struct MyStruct { char data[256]; @@ -143,6 +144,15 @@ void more_bounded_tests() { } } + { + long size = atol(getenv("USER")); + + if (size > 0) + { + malloc(size * sizeof(int)); // BAD + } + } + { int size = atoi(getenv("USER")); From b142ecb1dbc73e269b912719ca41f9c3684e56e5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 17 May 2021 10:33:06 +0200 Subject: [PATCH 379/550] C#: Address review comment --- csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 955775d0f66..3d7a1168e30 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -236,12 +236,6 @@ namespace Semmle.Autobuild.CSharp /// /// Gets the `dotnet build` script. - /// - /// The CLR tracer only works on .NET Core >= 3 on Linux and macOS (see - /// https://github.com/dotnet/coreclr/issues/19622), so in case we are - /// running on an older .NET Core, we disable shared compilation (and - /// hence the need for CLR tracing), by adding a - /// `/p:UseSharedCompilation=false` argument. /// private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { From 77c93dcf26fa083cc7fffc8e6b53a8bc59c169e1 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 17 May 2021 10:35:04 +0200 Subject: [PATCH 380/550] Make private --- .../code/java/frameworks/jackson/JacksonSerializability.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index 50266c377f8..6cda84512a0 100644 --- a/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/src/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -156,7 +156,7 @@ class JacksonDeserializableField extends DeserializableField { } /** A call to a field that may be deserialized using the Jackson JSON framework. */ -class JacksonDeserializableFieldAccess extends FieldAccess { +private class JacksonDeserializableFieldAccess extends FieldAccess { JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField } } @@ -164,7 +164,7 @@ class JacksonDeserializableFieldAccess extends FieldAccess { * When an object is deserialized by the Jackson JSON framework using a tainted input source, * the fields that the framework deserialized are themselves tainted input data. */ -class JacksonDeserializedTaintStep extends AdditionalTaintStep { +private class JacksonDeserializedTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1 } From 6853f6affaf5e222c99dd4deb3ca1e73ba14f49e Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 17 May 2021 15:53:57 +0200 Subject: [PATCH 381/550] C#: Fix type of temp foreach variable in IR --- .../raw/internal/desugar/Common.qll | 10 +++- .../raw/internal/desugar/Foreach.qll | 27 ++++++++-- .../test/experimental/ir/ir/raw_ir.expected | 54 +++++++++---------- .../ir/ir/raw_ir_consistency.expected | 3 -- .../ir/ir/unaliased_ssa_consistency.expected | 3 -- 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll index 267cf903b00..ef40d716fea 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll @@ -182,15 +182,21 @@ abstract class TranslatedCompilerGeneratedVariableAccess extends TranslatedCompi override Instruction getChildSuccessor(TranslatedElement child) { none() } + /** + * Returns the type of the accessed variable. Can be overriden when the return + * type is different than the type of the underlying variable. + */ + Type getVariableType() { result = getResultType() } + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = AddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getTypeForGLValue(getResultType()) + resultType = getTypeForGLValue(getVariableType()) or needsLoad() and tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = getTypeForPRValue(getResultType()) + resultType = getTypeForPRValue(getVariableType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll index a526ec5bdcd..195072ed124 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll @@ -359,9 +359,16 @@ private class TranslatedMoveNextEnumAcc extends TTranslatedCompilerGeneratedElem override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -384,9 +391,16 @@ private class TranslatedForeachCurrentEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -409,9 +423,16 @@ private class TranslatedForeachDisposeEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index c598a8778d9..5d3a1dfe6bf 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -613,48 +613,48 @@ foreach.cs: # 5| r5_28(glval) = PointerAdd[4] : r5_1, r5_27 # 5| r5_29(Int32) = Constant[7] : # 5| mu5_30(Int32) = Store[?] : &:r5_28, r5_29 -# 7| r7_1(glval) = VariableAddress[#temp7:9] : +# 7| r7_1(glval) = VariableAddress[#temp7:9] : # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load[a_array] : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : # 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? -# 7| mu7_7(Boolean) = Store[#temp7:9] : &:r7_1, r7_5 +# 7| mu7_7(IEnumerator) = Store[#temp7:9] : &:r7_1, r7_5 #-----| Goto -> Block 1 # 7| Block 1 -# 7| r7_8(glval) = VariableAddress[#temp7:9] : -# 7| r7_9(Boolean) = Load[#temp7:9] : &:r7_8, ~m? -# 7| r7_10() = FunctionAddress[MoveNext] : -# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 -# 7| mu7_12() = ^CallSideEffect : ~m? -# 7| v7_13(Void) = ConditionalBranch : r7_11 +# 7| r7_8(glval) = VariableAddress[#temp7:9] : +# 7| r7_9(IEnumerator) = Load[#temp7:9] : &:r7_8, ~m? +# 7| r7_10() = FunctionAddress[MoveNext] : +# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 +# 7| mu7_12() = ^CallSideEffect : ~m? +# 7| v7_13(Void) = ConditionalBranch : r7_11 #-----| False -> Block 3 #-----| True -> Block 2 # 7| Block 2 -# 7| r7_14(glval) = VariableAddress[items] : -# 7| r7_15(glval) = VariableAddress[#temp7:9] : -# 7| r7_16(Boolean) = Load[#temp7:9] : &:r7_15, ~m? -# 7| r7_17() = FunctionAddress[get_Current] : -# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 -# 7| mu7_19() = ^CallSideEffect : ~m? -# 7| mu7_20(Int32) = Store[items] : &:r7_14, r7_18 -# 9| r9_1(glval) = VariableAddress[x] : -# 9| r9_2(glval) = VariableAddress[items] : -# 9| r9_3(Int32) = Load[items] : &:r9_2, ~m? -# 9| mu9_4(Int32) = Store[x] : &:r9_1, r9_3 +# 7| r7_14(glval) = VariableAddress[items] : +# 7| r7_15(glval) = VariableAddress[#temp7:9] : +# 7| r7_16(IEnumerator) = Load[#temp7:9] : &:r7_15, ~m? +# 7| r7_17() = FunctionAddress[get_Current] : +# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 +# 7| mu7_19() = ^CallSideEffect : ~m? +# 7| mu7_20(Int32) = Store[items] : &:r7_14, r7_18 +# 9| r9_1(glval) = VariableAddress[x] : +# 9| r9_2(glval) = VariableAddress[items] : +# 9| r9_3(Int32) = Load[items] : &:r9_2, ~m? +# 9| mu9_4(Int32) = Store[x] : &:r9_1, r9_3 #-----| Goto (back edge) -> Block 1 # 7| Block 3 -# 7| r7_21(glval) = VariableAddress[#temp7:9] : -# 7| r7_22(Boolean) = Load[#temp7:9] : &:r7_21, ~m? -# 7| r7_23() = FunctionAddress[Dispose] : -# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 -# 7| mu7_25() = ^CallSideEffect : ~m? -# 4| v4_3(Void) = ReturnVoid : -# 4| v4_4(Void) = AliasedUse : ~m? -# 4| v4_5(Void) = ExitFunction : +# 7| r7_21(glval) = VariableAddress[#temp7:9] : +# 7| r7_22(IEnumerator) = Load[#temp7:9] : &:r7_21, ~m? +# 7| r7_23() = FunctionAddress[Dispose] : +# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 +# 7| mu7_25() = ^CallSideEffect : ~m? +# 4| v4_3(Void) = ReturnVoid : +# 4| v4_4(Void) = AliasedUse : ~m? +# 4| v4_5(Void) = ExitFunction : func_with_param_call.cs: # 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32) diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected index b2f9c0da160..7231134d5e2 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected @@ -26,9 +26,6 @@ fieldAddressOnNonPointer | inoutref.cs:19:13:19:17 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | inoutref.cs:16:17:16:17 | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | | pointers.cs:35:17:35:24 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | thisArgumentIsNonPointer -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | missingCanonicalLanguageType diff --git a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected index b2f9c0da160..7231134d5e2 100644 --- a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected @@ -26,9 +26,6 @@ fieldAddressOnNonPointer | inoutref.cs:19:13:19:17 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | inoutref.cs:16:17:16:17 | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | | pointers.cs:35:17:35:24 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | thisArgumentIsNonPointer -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | missingCanonicalLanguageType From 9e75f53798d9093332d40deaeda645dd514ec05d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 17 May 2021 15:35:19 +0100 Subject: [PATCH 382/550] C++: Prefer matches to regexpMatch. --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 8c9c4b3beee..361eb5695d2 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -68,7 +68,7 @@ predicate isInsecureEncryption(string name) { */ bindingset[name] predicate isEncryptionAdditionalEvidence(string name) { - name.toUpperCase().regexpMatch(".*(CRYPT|CODE|CODING|CBC|KEY).*") + name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY"] + "%") } /** From 57354def9ebaf4b4577e7e901e24dfa8ff3f1767 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 17 May 2021 15:36:27 +0100 Subject: [PATCH 383/550] C++: Real world diffs suggest that 'Cipher' should be an encryption word as well. --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 361eb5695d2..49cdd2d7c63 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -68,7 +68,7 @@ predicate isInsecureEncryption(string name) { */ bindingset[name] predicate isEncryptionAdditionalEvidence(string name) { - name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY"] + "%") + name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY", "CIPHER"] + "%") } /** From 930b9fe3e5e1ed1b9b59987b97cd99ff05a6b647 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 17 May 2021 15:46:41 +0100 Subject: [PATCH 384/550] C++: Add triple-DES to the bad algorithms list. --- cpp/ql/src/semmle/code/cpp/security/Encryption.qll | 10 +++------- .../CWE/CWE-327/BrokenCryptoAlgorithm.expected | 4 ++++ .../test/query-tests/Security/CWE/CWE-327/test.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 49cdd2d7c63..68a3d841d01 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -10,7 +10,8 @@ import cpp string getAnInsecureAlgorithmName() { result = [ - "DES", "RC2", "RC4", "RC5", "ARCFOUR" // ARCFOUR is a variant of RC4 + "DES", "RC2", "RC4", "RC5", "ARCFOUR", // ARCFOUR is a variant of RC4 + "3DES", "DES3" // also appears separated, e.g. "TRIPLE-DES", which will be matched as "DES". ] } @@ -53,12 +54,7 @@ string getInsecureAlgorithmRegex() { * insecure encyption algorithm. */ bindingset[name] -predicate isInsecureEncryption(string name) { - name.regexpMatch(getInsecureAlgorithmRegex()) and - // Check for evidence that an otherwise matching name may in fact not be - // related to insecure encrpytion, e.g. "Triple-DES" is not "DES". - not name.toUpperCase().regexpMatch(".*TRIPLE.*") -} +predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgorithmRegex()) } /** * Holds if there is additional evidence that `name` looks like it might be diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected index e213a6e92c3..79943db5f60 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -10,11 +10,15 @@ | test2.cpp:239:5:239:11 | call to encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:41:2:41:32 | ENCRYPT_WITH_3DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | +| test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:54:2:54:26 | DES_SET_KEY(data,amount) | This macro invocation specifies a broken or weak cryptographic algorithm. | | test.cpp:88:2:88:11 | call to encryptDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:89:2:89:11 | call to encryptRC2 | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:91:2:91:12 | call to encrypt3DES | This function call specifies a broken or weak cryptographic algorithm. | +| test.cpp:92:2:92:17 | call to encryptTripleDES | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:101:2:101:15 | call to do_des_encrypt | This function call specifies a broken or weak cryptographic algorithm. | | test.cpp:102:2:102:12 | call to DES_Set_Key | This function call specifies a broken or weak cryptographic algorithm. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index ede8f19b761..39005f63bd9 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -38,15 +38,15 @@ void test_macros(void *data, size_t amount, const char *str) ENCRYPT_WITH_DES(data, amount); // BAD ENCRYPT_WITH_RC2(data, amount); // BAD ENCRYPT_WITH_AES(data, amount); // GOOD (good algorithm) - ENCRYPT_WITH_3DES(data, amount); // GOOD (good enough algorithm) - ENCRYPT_WITH_TRIPLE_DES(data, amount); // GOOD (good enough algorithm) + ENCRYPT_WITH_3DES(data, amount); // BAD + ENCRYPT_WITH_TRIPLE_DES(data, amount); // BAD ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) DESENCRYPT(data, amount); // BAD [NOT DETECTED] RC2ENCRYPT(data, amount); // BAD [NOT DETECTED] AESENCRYPT(data, amount); // GOOD (good algorithm) - DES3ENCRYPT(data, amount); // GOOD (good enough algorithm) + DES3ENCRYPT(data, amount); // BAD [NOT DETECTED] DES_DO_ENCRYPTION(data, amount); // BAD RUN_DES_ENCODING(data, amount); // BAD @@ -88,13 +88,13 @@ void test_functions(void *data, size_t amount, const char *str) encryptDES(data, amount); // BAD encryptRC2(data, amount); // BAD encryptAES(data, amount); // GOOD (good algorithm) - encrypt3DES(data, amount); // GOOD (good enough algorithm) - encryptTripleDES(data, amount); // GOOD (good enough algorithm) + encrypt3DES(data, amount); // BAD + encryptTripleDES(data, amount); // BAD DESEncrypt(data, amount); // BAD RC2Encrypt(data, amount); // BAD AESEncrypt(data, amount); // GOOD (good algorithm) - DES3Encrypt(data, amount); // GOOD (good enough algorithm) + DES3Encrypt(data, amount); // BAD [NOT DETECTED] DoDESEncryption(data, amount); // BAD [NOT DETECTED] encryptDes(data, amount); // BAD [NOT DETECTED] From 09d00b133e6d5d99472344b03787186d0cdbf011 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 17 May 2021 15:53:03 +0100 Subject: [PATCH 385/550] C++: Acknowledge another not detected result in tests. --- cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp index 39005f63bd9..76ab7ce3423 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -91,8 +91,8 @@ void test_functions(void *data, size_t amount, const char *str) encrypt3DES(data, amount); // BAD encryptTripleDES(data, amount); // BAD - DESEncrypt(data, amount); // BAD - RC2Encrypt(data, amount); // BAD + DESEncrypt(data, amount); // BAD [NOT DETECTED] + RC2Encrypt(data, amount); // BAD [NOT DETECTED] AESEncrypt(data, amount); // GOOD (good algorithm) DES3Encrypt(data, amount); // BAD [NOT DETECTED] From 3b29920255f01305ee9890a8ad8dcee6cc2b5350 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 17 May 2021 16:10:39 +0100 Subject: [PATCH 386/550] C++: Replace getAChild with getAnArgument(). --- cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index 0d311333fca..04dda58cbf7 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -79,14 +79,14 @@ class InsecureFunctionCall extends FunctionCall { explain = "function call" or exists(MacroInvocation mi | - mi.getAGeneratedElement() = this.getAChild*() and + mi.getAGeneratedElement() = this.getAnArgument() and mi.getMacro() = getAnInsecureEncryptionMacro() and blame = mi and explain = "macro invocation" ) or exists(EnumConstantAccess ec | - ec = this.getAChild*() and + ec = this.getAnArgument() and ec.getTarget() = getAnInsecureEncryptionEnumConst() and blame = ec and explain = "enum constant access" @@ -97,12 +97,12 @@ class InsecureFunctionCall extends FunctionCall { getTarget() = getAdditionalEvidenceFunction() or exists(MacroInvocation mi | - mi.getAGeneratedElement() = this.getAChild*() and + mi.getAGeneratedElement() = this.getAnArgument() and mi.getMacro() = getAdditionalEvidenceMacro() ) or exists(EnumConstantAccess ec | - ec = this.getAChild*() and + ec = this.getAnArgument() and ec.getTarget() = getAdditionalEvidenceEnumConst() ) ) From 0ad69d11a8408625ea0a4e2b3ca8f6bf2bc87090 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Mon, 17 May 2021 18:39:33 +0100 Subject: [PATCH 387/550] Code Scanning selectors: Include diagnostic queries --- misc/suite-helpers/code-scanning-selectors.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/misc/suite-helpers/code-scanning-selectors.yml b/misc/suite-helpers/code-scanning-selectors.yml index a57aa15679e..116d7288ddf 100644 --- a/misc/suite-helpers/code-scanning-selectors.yml +++ b/misc/suite-helpers/code-scanning-selectors.yml @@ -13,6 +13,9 @@ - warning tags contain: - security +- include: + kind: + - diagnostic - include: kind: - metric From ef410b998431c1467d10341e9581bf639f6f6b80 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 17 May 2021 19:27:10 +0100 Subject: [PATCH 388/550] Update java/change-notes/2021-05-14-close-resource-leaks-improvements.md --- .../2021-05-14-close-resource-leaks-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md index bae8640d85a..08a77840a1f 100644 --- a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md +++ b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md @@ -1,2 +1,2 @@ lgtm,codescanning -* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries have been made more specific to only consider JDK classes and subtypes. Additionally the number of false positives has been reduced. +* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries no longer confuse `java.io` classes such as `Reader` with others that happen to share the same base name. Additionally the number of false positives has been reduced by recognizing `CharArrayReader` and `CharArrayWriter` as types that don't need to be closed. From e652d8771c343c819d62d1901593185af2506d25 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 17 May 2021 20:34:16 +0000 Subject: [PATCH 389/550] Update method name and qldoc --- .../Security/CWE/CWE-094/JythonInjection.ql | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index 0f23edc69f4..06b077446e2 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -52,7 +52,7 @@ class LoadClassMethod extends Method { * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array * representing the class to be loaded. */ -predicate loadClass(MethodAccess ma, Expr sink) { +predicate loadsClass(MethodAccess ma, Expr sink) { exists(Method m, int i | m = ma.getMethod() | m instanceof LoadClassMethod and m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) @@ -85,17 +85,21 @@ predicate compile(MethodAccess ma, Expr sink) { class CodeInjectionSink extends DataFlow::ExprNode { CodeInjectionSink() { runCode(_, this.getExpr()) or - loadClass(_, this.getExpr()) or + loadsClass(_, this.getExpr()) or compile(_, this.getExpr()) } MethodAccess getMethodAccess() { runCode(result, this.getExpr()) or - loadClass(result, this.getExpr()) or + loadsClass(result, this.getExpr()) or compile(result, this.getExpr()) } } +/** + * A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call + * `CodeInjectionSink` that executes injected code. + */ class CodeInjectionConfiguration extends TaintTracking::Configuration { CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } From a0cd551bae9ae005c59657ef3463817e226cfe29 Mon Sep 17 00:00:00 2001 From: haby0 Date: Tue, 18 May 2021 11:05:10 +0800 Subject: [PATCH 390/550] Add filtering of String.format --- .../Security/CWE/CWE-601/SpringUrlRedirect.ql | 10 +++ .../CWE/CWE-601/SpringUrlRedirect.qll | 61 +------------------ .../CWE-601/SpringUrlRedirect.expected | 8 +++ .../security/CWE-601/SpringUrlRedirect.java | 17 +++++- 4 files changed, 37 insertions(+), 59 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql index 138bce57ac9..5c8a8ea78d8 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql @@ -33,6 +33,16 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration { ae.getRightOperand() = node.asExpr() and not ae instanceof RedirectBuilderExpr ) + or + exists(MethodAccess ma, int index | + ma.getMethod().hasName("format") and + ma.getMethod().getDeclaringType() instanceof TypeString and + ma.getArgument(index) = node.asExpr() and + ( + index != 0 and + not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*") + ) + ) } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll index 866eaae1c34..84bb5d9adf8 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-601/SpringUrlRedirect.qll @@ -51,14 +51,14 @@ class SpringUrlRedirectSink extends DataFlow::Node { SpringUrlRedirectSink() { exists(RedirectBuilderExpr rbe | rbe.getRightOperand() = this.asExpr() and - exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(rbe), _)) + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) ) or exists(MethodAccess ma, RedirectAppendCall rac | DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and ma.getMethod().hasName("append") and ma.getArgument(0) = this.asExpr() and - exists(RedirectBuilderFlowConfig rbfc | rbfc.hasFlow(exprNode(ma.getQualifier()), _)) + any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) ) or exists(MethodAccess ma | @@ -66,8 +66,7 @@ class SpringUrlRedirectSink extends DataFlow::Node { ma.getMethod() .getDeclaringType() .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and - ma.getArgument(0) = this.asExpr() and - exists(RedirectViewFlowConfig rvfc | rvfc.hasFlowToExpr(ma.getQualifier())) + ma.getArgument(0) = this.asExpr() ) or exists(ClassInstanceExpr cie | @@ -84,57 +83,3 @@ class SpringUrlRedirectSink extends DataFlow::Node { ) } } - -/** A data flow configuration tracing flow from redirect builder expression to spring controller method return expression. */ -private class RedirectBuilderFlowConfig extends DataFlow2::Configuration { - RedirectBuilderFlowConfig() { this = "RedirectBuilderFlowConfig" } - - override predicate isSource(DataFlow::Node src) { - exists(RedirectBuilderExpr rbe | rbe = src.asExpr()) - or - exists(MethodAccess ma, RedirectAppendCall rac | - DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and - ma.getMethod().hasName("append") and - ma.getQualifier() = src.asExpr() - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(ReturnStmt rs, SpringRequestMappingMethod sqmm | - rs.getResult() = sink.asExpr() and - sqmm.getBody().getAStmt() = rs - ) - } - - override predicate isAdditionalFlowStep(Node prod, Node succ) { - exists(MethodAccess ma | - ma.getMethod().hasName("toString") and - ma.getMethod().getDeclaringType() instanceof StringBuildingType and - ma.getQualifier() = prod.asExpr() and - ma = succ.asExpr() - ) - } -} - -/** A data flow configuration tracing flow from RedirectView object to calling setUrl method. */ -private class RedirectViewFlowConfig extends DataFlow2::Configuration { - RedirectViewFlowConfig() { this = "RedirectViewFlowConfig" } - - override predicate isSource(DataFlow::Node src) { - exists(ClassInstanceExpr cie | - cie.getConstructedType() - .hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and - cie = src.asExpr() - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - ma.getMethod().hasName("setUrl") and - ma.getMethod() - .getDeclaringType() - .hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and - ma.getQualifier() = sink.asExpr() - ) - } -} diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected index 26b8acd7770..bcf5e892e1b 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.expected @@ -5,6 +5,8 @@ edges | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | +| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | +| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | nodes | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl | @@ -18,6 +20,10 @@ nodes | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl | | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String | | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl | +| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:54:30:54:66 | format(...) | semmle.label | format(...) | +| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | semmle.label | redirectUrl : String | +| SpringUrlRedirect.java:59:30:59:76 | format(...) | semmle.label | format(...) | #select | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value | @@ -25,3 +31,5 @@ nodes | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value | | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value | | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:54:30:54:66 | format(...) | SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:53:24:53:41 | redirectUrl | user-provided value | +| SpringUrlRedirect.java:59:30:59:76 | format(...) | SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:58:24:58:41 | redirectUrl | user-provided value | diff --git a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java index 5124d8cd8c6..f3958cba102 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java +++ b/java/ql/test/experimental/query-tests/security/CWE-601/SpringUrlRedirect.java @@ -50,6 +50,16 @@ public class SpringUrlRedirect { } @GetMapping("url7") + public String bad7(String redirectUrl) { + return "redirect:" + String.format("%s/?aaa", redirectUrl); + } + + @GetMapping("url8") + public String bad8(String redirectUrl, String token) { + return "redirect:" + String.format(redirectUrl + "?token=%s", token); + } + + @GetMapping("url9") public RedirectView good1(String redirectUrl) { RedirectView rv = new RedirectView(); if (redirectUrl.startsWith(VALID_REDIRECT)){ @@ -60,9 +70,14 @@ public class SpringUrlRedirect { return rv; } - @GetMapping("url8") + @GetMapping("url10") public ModelAndView good2(String token) { String url = "/edit?token=" + token; return new ModelAndView("redirect:" + url); } + + @GetMapping("url11") + public String good3(String status) { + return "redirect:" + String.format("/stories/search/criteria?status=%s", status); + } } From cac0ab299ba20e838163d44b1ed9f41c0480f9d8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 18 May 2021 10:25:25 +0200 Subject: [PATCH 391/550] add writes to textContent on a