From abd6df3047a327d62c89a60ff565d2eee8292fa6 Mon Sep 17 00:00:00 2001 From: yh-semmle Date: Wed, 5 Sep 2018 20:16:18 -0400 Subject: [PATCH 001/110] Java: add Semmle/java team to CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 503c68f963ed..d6d1d4898a64 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,3 @@ /csharp/ @Semmle/cs +/java/ @Semmle/java /javascript/ @Semmle/js From 91797012482b1d4b308735febb4307f5a7b77fee Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Thu, 6 Sep 2018 11:38:08 -0700 Subject: [PATCH 002/110] JavaScript: Add query for Node.js integration in Electron framework --- .../Electron/EnablingNodeIntegration.qhelp | 56 +++++++++++++++++++ .../src/Electron/EnablingNodeIntegration.ql | 28 ++++++++++ .../examples/DefaultNodeIntegration.js | 2 + .../examples/EnablingNodeIntegration.js | 26 +++++++++ .../examples/WebViewNodeIntegration.html | 15 +++++ .../EnablingNodeIntegration.expected | 5 ++ .../EnablingNodeIntegration.js | 51 +++++++++++++++++ .../EnablingNodeIntegration.qlref | 1 + 8 files changed, 184 insertions(+) create mode 100644 javascript/ql/src/Electron/EnablingNodeIntegration.qhelp create mode 100644 javascript/ql/src/Electron/EnablingNodeIntegration.ql create mode 100644 javascript/ql/src/Electron/examples/DefaultNodeIntegration.js create mode 100644 javascript/ql/src/Electron/examples/EnablingNodeIntegration.js create mode 100644 javascript/ql/src/Electron/examples/WebViewNodeIntegration.html create mode 100644 javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected create mode 100644 javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js create mode 100644 javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp new file mode 100644 index 000000000000..0e3ad814d43b --- /dev/null +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -0,0 +1,56 @@ + + + + +

+ Enabling Node.js integration in web content renderers (BrowserWindow, BrowserView and webview) could result in + remote native code execution attacks when rendering malicious JavaScript code from untrusted remote web site or + code that is injected via a cross site scripting vulnerability into the web content under processing. Please note that + the nodeIntegration property is enabled by default in Electron and needs to be set to 'false' explicitly. +

+
+ + +

+ Node.js integration should be disabled when loading remote web sites. If not possible, always set nodeIntegration property + to 'false' before loading remote web sites and only enable it for whitelisted sites. +

+
+ + +

+ The following example shows insecure use of BrowserWindow with regards to nodeIntegration + property: +

+ + +

+ This is problematic, because default value of nodeIntegration is 'true'. +

+ +
+ + +

+ The following example shows insecure and secure uses of tag: +

+ + +
+ + +

+ The following example shows insecure and secure uses of BrowserWindow and BrowserView when + loading untrusted web sites: +

+ + +
+ + + +
  • Electron Documentation: Security, Native Capabilities, and Your Responsibility
  • +
    +
    diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.ql b/javascript/ql/src/Electron/EnablingNodeIntegration.ql new file mode 100644 index 000000000000..39b19c5d43b7 --- /dev/null +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.ql @@ -0,0 +1,28 @@ +/** + * @name Enabling nodeIntegration and nodeIntegrationInWorker in webPreferences + * @description Enabling nodeIntegration and nodeIntegrationInWorker could expose your app to remote code execution. + * @kind problem + * @problem.severity warning + * @precision very-high + * @tags security + * frameworks/electron + * @id js/enabling-electron-renderer-node-integration + */ + +import javascript + +string checkWebOptions(DataFlow::PropWrite prop, Electron::WebPreferences pref) { + (prop = pref.getAPropertyWrite("nodeIntegration") and + prop.getRhs().mayHaveBooleanValue(true) and + result = "nodeIntegration property may have been enabled on this object that could result in RCE") + or + (prop = pref.getAPropertyWrite("nodeIntegrationInWorker") and + prop.getRhs().mayHaveBooleanValue(true) and + result = "nodeIntegrationInWorker property may have been enabled on this object that could result in RCE") + or + (not exists(pref.asExpr().(ObjectExpr).getPropertyByName("nodeIntegration")) and + result = "nodeIntegration is enabled by default in WebPreferences object that could result in RCE") +} + +from DataFlow::PropWrite property, Electron::WebPreferences preferences +select preferences,checkWebOptions(property, preferences) \ No newline at end of file diff --git a/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js b/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js new file mode 100644 index 000000000000..781d31f59f6f --- /dev/null +++ b/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js @@ -0,0 +1,2 @@ +const win = new BrowserWindow(); +win.loadURL("https://untrusted-site.com"); \ No newline at end of file diff --git a/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js b/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js new file mode 100644 index 000000000000..7a6312a807b5 --- /dev/null +++ b/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js @@ -0,0 +1,26 @@ +//BAD +win_1 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); +win_1.loadURL("https://untrusted-site.com"); + +//GOOD +win_2 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: false}}); +win_2.loadURL("https://untrusted-site.com"); + +//BAD +win_3 = new BrowserWindow({ + webPreferences: { + nodeIntegrationInWorker: true + } +}); + +//BAD BrowserView +win_4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: false}}) +view = new BrowserView({ + webPreferences: { + nodeIntegration: true + } +}); +win.setBrowserView(view); +view.setBounds({ x: 0, y: 0, width: 300, height: 300 }); +view.webContents.loadURL('https://untrusted-site.com'); + diff --git a/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html b/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html new file mode 100644 index 000000000000..1deaa6de3326 --- /dev/null +++ b/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html @@ -0,0 +1,15 @@ + + + + + WebView Examples + + + + + + + + + + \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected new file mode 100644 index 000000000000..c2d2cd5684f3 --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected @@ -0,0 +1,5 @@ +| EnablingNodeIntegration.js:5:28:11:9 | {\\r\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\r\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:15:22:20:9 | {\\r\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:23:13:27:9 | {\\r\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:49:71:49:93 | {nodeIn ... : true} | nodeIntegration property may have been enabled on this object that could result in RCE | diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js new file mode 100644 index 000000000000..fd1a4201df7a --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js @@ -0,0 +1,51 @@ +const {BrowserWindow} = require('electron') + +function test() { + var unsafe_1 = { + webPreferences: { + nodeIntegration: true, + nodeIntegrationInWorker: true, + plugins: true, + webSecurity: true, + sandbox: true + } + }; + + var options_1 = { + webPreferences: { + plugins: true, + nodeIntegrationInWorker: false, + webSecurity: true, + sandbox: true + } + }; + + var pref = { + plugins: true, + webSecurity: true, + sandbox: true + }; + + var options_2 = { + webPreferences: pref, + show: true, + frame: true, + minWidth: 300, + minHeight: 300 + }; + + var safe_used = { + webPreferences: { + nodeIntegration: false, + plugins: true, + webSecurity: true, + sandbox: true + } + }; + + var w1 = new BrowserWindow(unsafe_1); + var w2 = new BrowserWindow(options_1); + var w3 = new BrowserWindow(safe_used); + var w4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); + var w5 = new BrowserWindow(options_2); +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref new file mode 100644 index 000000000000..3f8dbad0d571 --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref @@ -0,0 +1 @@ +../../../../src/Electron/EnablingNodeIntegration.ql \ No newline at end of file From ebbd3b3111691edda50b2dd7f2a9074beb14a19a Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Fri, 7 Sep 2018 08:47:35 -0700 Subject: [PATCH 003/110] Adding html encoding to EnablingNodeIntegration.qhelp --- javascript/ql/src/Electron/EnablingNodeIntegration.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp index 0e3ad814d43b..03cb7238a748 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -34,7 +34,7 @@

    - The following example shows insecure and secure uses of tag: + The following example shows insecure and secure uses of <webview> tag:

    From bd92cd14c5e53102904fb7c2f5697dc11a158cb5 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Fri, 7 Sep 2018 09:47:15 -0700 Subject: [PATCH 004/110] Changing EOL in all files to unix format --- javascript/ql/src/Electron/EnablingNodeIntegration.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp index 03cb7238a748..3ac30289f17b 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -34,7 +34,7 @@

    - The following example shows insecure and secure uses of <webview> tag: + The following example shows insecure and secure uses of webview tag:

    From 302e271a790027eee2eb33d97c0d7a9e505caea5 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi <43017278+bnxi@users.noreply.github.com> Date: Fri, 7 Sep 2018 09:52:52 -0700 Subject: [PATCH 005/110] Update EnablingNodeIntegration.expected Change EOL to unix format --- .../NodeIntegration/EnablingNodeIntegration.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected index c2d2cd5684f3..f43bc66ef6ba 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected @@ -1,5 +1,5 @@ -| EnablingNodeIntegration.js:5:28:11:9 | {\\r\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:5:28:11:9 | {\\r\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:15:22:20:9 | {\\r\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | -| EnablingNodeIntegration.js:23:13:27:9 | {\\r\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:23:13:27:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | | EnablingNodeIntegration.js:49:71:49:93 | {nodeIn ... : true} | nodeIntegration property may have been enabled on this object that could result in RCE | From 43a9d511c22be0535a6d3b454fc556c2d4215c90 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi <43017278+bnxi@users.noreply.github.com> Date: Fri, 7 Sep 2018 14:58:24 -0700 Subject: [PATCH 006/110] Update EnablingNodeIntegration.qhelp --- javascript/ql/src/Electron/EnablingNodeIntegration.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp index 3ac30289f17b..47337a03b4e1 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -34,7 +34,7 @@

    - The following example shows insecure and secure uses of webview tag: + The following example shows insecure and secure uses of webview tag:

    From 02047ea26063ed247315d3abde92e78a8ffc1cb9 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Mon, 10 Sep 2018 10:27:29 -0700 Subject: [PATCH 007/110] Edit .expected file --- .../NodeIntegration/EnablingNodeIntegration.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected index f43bc66ef6ba..092ad577de35 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected @@ -1,5 +1,5 @@ -| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | -| EnablingNodeIntegration.js:23:13:27:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | +| EnablingNodeIntegration.js:23:13:27:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | | EnablingNodeIntegration.js:49:71:49:93 | {nodeIn ... : true} | nodeIntegration property may have been enabled on this object that could result in RCE | From 2d7109b8f5a5b3fc1343c4c40389168f9708dd9c Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 23 Aug 2018 17:45:30 -0400 Subject: [PATCH 008/110] C++: initial implementation of a HashCons library. --- .../code/cpp/valuenumbering/HashCons.qll | 404 ++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll new file mode 100644 index 000000000000..3bfc8654ae15 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -0,0 +1,404 @@ +/** + * Provides an implementation of Hash consing. + * See https://en.wikipedia.org/wiki/Hash_consing + * + * The predicate `hashCons` converts an expression into a `HC`, which is an + * abstract type presenting the hash-cons of the expression. If two + * expressions have the same `HC` then they are structurally equal. + * + * Important note: this library ignores the possibility that the value of + * an expression might change between one occurrence and the next. For + * example: + * + * ``` + * x = a+b; + * a++; + * y = a+b; + * ``` + * + * In this example, both copies of the expression `a+b` will hash-cons to + * the same value, even though the value of `a` has changed. This is the + * intended behavior of this library. If you care about the value of the + * expression being the same, then you should use the GlobalValueNumbering + * library instead. + * + * To determine if the expression `x` is structurally equal to the + * expression `y`, use the library like this: + * + * ``` + * hashCons(x) = hashCons(y) + * ``` + */ + +/* + * Note to developers: the correctness of this module depends on the + * definitions of HC, hashCons, and analyzableExpr being kept in sync with + * each other. If you change this module then make sure that the change is + * symmetric across all three. + */ + +import cpp + +/** Used to represent the hash-cons of an expression. */ +private cached newtype HCBase = + HC_IntConst(int val, Type t) { mk_IntConst(val,t,_) } + or + HC_FloatConst(float val, Type t) { mk_FloatConst(val,t,_) } + or + HC_Variable(Variable x) { + mk_Variable(x, _) + } + or + HC_FieldAccess(HC s, Field f) { + mk_DotFieldAccess(s,f,_) or + mk_PointerFieldAccess_with_deref(s,f,_) or + mk_ImplicitThisFieldAccess_with_deref(s,f,_) + } + or + HC_Deref(HC p) { + mk_Deref(p,_) or + mk_PointerFieldAccess(p,_,_) or + mk_ImplicitThisFieldAccess_with_qualifier(p,_,_) + } + or + HC_ThisExpr(Function fcn) { + mk_ThisExpr(fcn,_) or + mk_ImplicitThisFieldAccess(fcn,_,_) + } + or + HC_Conversion(Type t, HC child) { mk_Conversion(t, child, _) } + or + HC_BinaryOp(HC lhs, HC rhs, string opname) { + mk_BinaryOp(lhs, rhs, opname, _) + } + or + HC_UnaryOp(HC child, string opname) { mk_UnaryOp(child, opname, _) } + or + HC_ArrayAccess(HC x, HC i) { + mk_ArrayAccess(x,i,_) + } + or + // Any expression that is not handled by the cases above is + // given a unique number based on the expression itself. + HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } + +/** + * HC is the hash-cons of an expression. The relationship between `Expr` + * and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple + * expressions can have the same `HC`. If two expressions have the same + * `HC`, it means that they are structurally equal. The `HC` is an opaque + * value. The only use for the `HC` of an expression is to find other + * expressions that are structurally equal to it. Use the predicate + * `hashCons` to get the `HC` for an `Expr`. + * + * Note: `HC` has `toString` and `getLocation` methods, so that it can be + * displayed in a results list. These work by picking an arbitrary + * expression with this `HC` and using its `toString` and `getLocation` + * methods. + */ +class HC extends HCBase { + HC() { this instanceof HCBase } + + /** Gets an expression that has this HC. */ + Expr getAnExpr() { + this = hashCons(result) + } + + /** Gets the kind of the HC. This can be useful for debugging. */ + string getKind() { + if this instanceof HC_IntConst then result = "IntConst" else + if this instanceof HC_FloatConst then result = "FloatConst" else + if this instanceof HC_Variable then result = "Variable" else + if this instanceof HC_FieldAccess then result = "FieldAccess" else + if this instanceof HC_Deref then result = "Deref" else + if this instanceof HC_ThisExpr then result = "ThisExpr" else + if this instanceof HC_Conversion then result = "Conversion" else + if this instanceof HC_BinaryOp then result = "BinaryOp" else + if this instanceof HC_UnaryOp then result = "UnaryOp" else + if this instanceof HC_ArrayAccess then result = "ArrayAccess" else + if this instanceof HC_Unanalyzable then result = "Unanalyzable" else + result = "error" + } + + /** + * Gets an example of an expression with this HC. + * This is useful for things like implementing toString(). + */ + private Expr exampleExpr() { + // Pick the expression with the minimum source location string. This is + // just an arbitrary way to pick an expression with this `HC`. + result = + min(Expr e + | this = hashCons(e) + | e order by e.getLocation().toString()) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = exampleExpr().toString() + } + + /** Gets the primary location of this element. */ + Location getLocation() { + result = exampleExpr().getLocation() + } +} + +private predicate analyzableIntConst(Expr e) { + strictcount (e.getValue().toInt()) = 1 and + strictcount (e.getType().getUnspecifiedType()) = 1 +} + +private predicate mk_IntConst(int val, Type t, Expr e) { + analyzableIntConst(e) and + val = e.getValue().toInt() and + t = e.getType().getUnspecifiedType() +} + +private predicate analyzableFloatConst(Expr e) { + strictcount (e.getValue().toFloat()) = 1 and + strictcount (e.getType().getUnspecifiedType()) = 1 and + not analyzableIntConst(e) +} + +private predicate mk_FloatConst(float val, Type t, Expr e) { + analyzableFloatConst(e) and + val = e.getValue().toFloat() and + t = e.getType().getUnspecifiedType() +} + +private predicate analyzableDotFieldAccess(DotFieldAccess access) { + strictcount (access.getTarget()) = 1 and + strictcount (access.getQualifier().getFullyConverted()) = 1 and + not analyzableConst(access) +} + +private predicate mk_DotFieldAccess( + HC qualifier, Field target, DotFieldAccess access) { + analyzableDotFieldAccess(access) and + target = access.getTarget() and + qualifier = hashCons(access.getQualifier().getFullyConverted()) +} + +private predicate analyzablePointerFieldAccess(PointerFieldAccess access) { + strictcount (access.getTarget()) = 1 and + strictcount (access.getQualifier().getFullyConverted()) = 1 and + not analyzableConst(access) +} + +private predicate mk_PointerFieldAccess( + HC qualifier, Field target, + PointerFieldAccess access) { + analyzablePointerFieldAccess(access) and + target = access.getTarget() and + qualifier = hashCons(access.getQualifier().getFullyConverted()) +} + +/* + * `obj->field` is equivalent to `(*obj).field`, so we need to wrap an + * extra `HC_Deref` around the qualifier. + */ +private predicate mk_PointerFieldAccess_with_deref( + HC new_qualifier, Field target, PointerFieldAccess access) { + exists (HC qualifier + | mk_PointerFieldAccess(qualifier, target, access) and + new_qualifier = HC_Deref(qualifier)) +} + +private predicate analyzableImplicitThisFieldAccess( + ImplicitThisFieldAccess access) { + strictcount (access.getTarget()) = 1 and + strictcount (access.getEnclosingFunction()) = 1 and + not analyzableConst(access) +} + +private predicate mk_ImplicitThisFieldAccess( + Function fcn, Field target, + ImplicitThisFieldAccess access) { + analyzableImplicitThisFieldAccess(access) and + target = access.getTarget() and + fcn = access.getEnclosingFunction() +} + +private predicate mk_ImplicitThisFieldAccess_with_qualifier( + HC qualifier, Field target, + ImplicitThisFieldAccess access) { + exists (Function fcn + | mk_ImplicitThisFieldAccess(fcn, target, access) and + qualifier = HC_ThisExpr(fcn)) +} + +private predicate mk_ImplicitThisFieldAccess_with_deref( + HC new_qualifier, Field target, ImplicitThisFieldAccess access) { + exists (HC qualifier + | mk_ImplicitThisFieldAccess_with_qualifier( + qualifier, target, access) and + new_qualifier = HC_Deref(qualifier)) +} + +private predicate analyzableVariable(VariableAccess access) { + not (access instanceof FieldAccess) and + strictcount (access.getTarget()) = 1 and + not analyzableConst(access) +} + +private predicate mk_Variable(Variable x, VariableAccess access) { + analyzableVariable(access) and + x = access.getTarget() +} + +private predicate analyzableConversion(Conversion conv) { + strictcount (conv.getType().getUnspecifiedType()) = 1 and + strictcount (conv.getExpr()) = 1 and + not analyzableConst(conv) +} + +private predicate mk_Conversion(Type t, HC child, Conversion conv) { + analyzableConversion(conv) and + t = conv.getType().getUnspecifiedType() and + child = hashCons(conv.getExpr()) +} + +private predicate analyzableBinaryOp(BinaryOperation op) { + op.isPure() and + strictcount (op.getLeftOperand().getFullyConverted()) = 1 and + strictcount (op.getRightOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 and + not analyzableConst(op) +} + +private predicate mk_BinaryOp( + HC lhs, HC rhs, string opname, BinaryOperation op) { + analyzableBinaryOp(op) and + lhs = hashCons(op.getLeftOperand().getFullyConverted()) and + rhs = hashCons(op.getRightOperand().getFullyConverted()) and + opname = op.getOperator() +} + +private predicate analyzableUnaryOp(UnaryOperation op) { + not (op instanceof PointerDereferenceExpr) and + op.isPure() and + strictcount (op.getOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 and + not analyzableConst(op) +} + +private predicate mk_UnaryOp(HC child, string opname, UnaryOperation op) { + analyzableUnaryOp(op) and + child = hashCons(op.getOperand().getFullyConverted()) and + opname = op.getOperator() +} + +private predicate analyzableThisExpr(ThisExpr thisExpr) { + strictcount(thisExpr.getEnclosingFunction()) = 1 and + not analyzableConst(thisExpr) +} + +private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) { + analyzableThisExpr(thisExpr) and + fcn = thisExpr.getEnclosingFunction() +} + +private predicate analyzableArrayAccess(ArrayExpr ae) { + strictcount (ae.getArrayBase().getFullyConverted()) = 1 and + strictcount (ae.getArrayOffset().getFullyConverted()) = 1 and + not analyzableConst(ae) +} + +private predicate mk_ArrayAccess( + HC base, HC offset, ArrayExpr ae) { + analyzableArrayAccess(ae) and + base = hashCons(ae.getArrayBase().getFullyConverted()) and + offset = hashCons(ae.getArrayOffset().getFullyConverted()) +} + +private predicate analyzablePointerDereferenceExpr( + PointerDereferenceExpr deref) { + strictcount (deref.getOperand().getFullyConverted()) = 1 and + not analyzableConst(deref) +} + +private predicate mk_Deref( + HC p, PointerDereferenceExpr deref) { + analyzablePointerDereferenceExpr(deref) and + p = hashCons(deref.getOperand().getFullyConverted()) +} + +/** Gets the hash-cons of expression `e`. */ +cached HC hashCons(Expr e) { + exists (int val, Type t + | mk_IntConst(val, t, e) and + result = HC_IntConst(val, t)) + or + exists (float val, Type t + | mk_FloatConst(val, t, e) and + result = HC_FloatConst(val, t)) + or + // Variable with no SSA information. + exists (Variable x + | mk_Variable(x, e) and + result = HC_Variable(x)) + or + exists (HC qualifier, Field target + | mk_DotFieldAccess(qualifier, target, e) and + result = HC_FieldAccess(qualifier, target)) + or + exists (HC qualifier, Field target + | mk_PointerFieldAccess_with_deref(qualifier, target, e) and + result = HC_FieldAccess(qualifier, target)) + or + exists (HC qualifier, Field target + | mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and + result = HC_FieldAccess(qualifier, target)) + or + exists (Function fcn + | mk_ThisExpr(fcn, e) and + result = HC_ThisExpr(fcn)) + or + exists (Type t, HC child + | mk_Conversion(t, child, e) and + result = HC_Conversion(t, child)) + or + exists (HC lhs, HC rhs, string opname + | mk_BinaryOp(lhs, rhs, opname, e) and + result = HC_BinaryOp(lhs, rhs, opname)) + or + exists (HC child, string opname + | mk_UnaryOp(child, opname, e) and + result = HC_UnaryOp(child, opname)) + or + exists (HC x, HC i + | mk_ArrayAccess(x, i, e) and + result = HC_ArrayAccess(x, i)) + or + exists (HC p + | mk_Deref(p, e) and + result = HC_Deref(p)) + or + (not analyzableExpr(e,_) and result = HC_Unanalyzable(e)) +} + +private predicate analyzableConst(Expr e) { + analyzableIntConst(e) or + analyzableFloatConst(e) +} + +/** + * Holds if the expression is explicitly handled by `hashCons`. + * Unanalyzable expressions still need to be given a hash-cons, + * but it will be a unique number that is not shared with any other + * expression. + */ +predicate analyzableExpr(Expr e, string kind) { + (analyzableConst(e) and kind = "Const") or + (analyzableDotFieldAccess(e) and kind = "DotFieldAccess") or + (analyzablePointerFieldAccess(e) and kind = "PointerFieldAccess") or + (analyzableImplicitThisFieldAccess(e) and kind = "ImplicitThisFieldAccess") or + (analyzableVariable(e) and kind = "Variable") or + (analyzableConversion(e) and kind = "Conversion") or + (analyzableBinaryOp(e) and kind = "BinaryOp") or + (analyzableUnaryOp(e) and kind = "UnaryOp") or + (analyzableThisExpr(e) and kind = "ThisExpr") or + (analyzableArrayAccess(e) and kind = "ArrayAccess") or + (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") +} From 3c6a9c08a2ff571e5c9cc604218656dbe522ea92 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Aug 2018 15:25:16 -0700 Subject: [PATCH 009/110] C++: first tests for HashCons --- .../HashCons/GlobalValueNumbering.expected | 35 ++++++ .../HashCons/GlobalValueNumbering.ql | 12 +++ .../HashCons/Uniqueness.expected | 0 .../valuenumbering/HashCons/Uniqueness.ql | 8 ++ .../valuenumbering/HashCons/test.cpp | 100 ++++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected create mode 100644 cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql create mode 100644 cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.expected create mode 100644 cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql create mode 100644 cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected new file mode 100644 index 000000000000..7807cc607ad0 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected @@ -0,0 +1,35 @@ +| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 7:c7-c7 | +| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 | +| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 | +| test.cpp:5:12:5:13 | p1 | 5:c12-c13 6:c12-c13 | +| test.cpp:16:3:16:3 | x | 16:c3-c3 17:c3-c3 18:c7-c7 | +| test.cpp:16:7:16:8 | p0 | 16:c7-c8 17:c7-c8 | +| test.cpp:16:7:16:13 | ... + ... | 16:c7-c13 17:c7-c13 | +| test.cpp:16:7:16:24 | ... + ... | 16:c7-c24 17:c7-c24 | +| test.cpp:16:12:16:13 | p1 | 16:c12-c13 17:c12-c13 | +| test.cpp:16:17:16:24 | global01 | 16:c17-c24 17:c17-c24 | +| test.cpp:29:3:29:3 | x | 29:c3-c3 31:c3-c3 32:c7-c7 | +| test.cpp:29:7:29:8 | p0 | 29:c7-c8 31:c7-c8 | +| test.cpp:29:7:29:13 | ... + ... | 29:c7-c13 31:c7-c13 | +| test.cpp:29:7:29:24 | ... + ... | 29:c7-c24 31:c7-c24 | +| test.cpp:29:12:29:13 | p1 | 29:c12-c13 31:c12-c13 | +| test.cpp:29:17:29:24 | global02 | 29:c17-c24 31:c17-c24 | +| test.cpp:43:3:43:3 | x | 43:c3-c3 45:c3-c3 46:c7-c7 | +| test.cpp:43:7:43:8 | p0 | 43:c7-c8 45:c7-c8 | +| test.cpp:43:7:43:13 | ... + ... | 43:c7-c13 45:c7-c13 | +| test.cpp:43:7:43:24 | ... + ... | 43:c7-c24 45:c7-c24 | +| test.cpp:43:12:43:13 | p1 | 43:c12-c13 45:c12-c13 | +| test.cpp:43:17:43:24 | global03 | 43:c17-c24 45:c17-c24 | +| test.cpp:44:9:44:9 | 0 | 44:c9-c9 51:c25-c25 53:c18-c21 56:c39-c42 59:c17-c20 88:c12-c12 | +| test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 | +| test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 | +| test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 | +| test.cpp:53:18:53:21 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 | +| test.cpp:55:5:55:7 | ptr | 55:c5-c7 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 | +| test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 | +| test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 | +| test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 | +| test.cpp:79:7:79:7 | v | 79:c7-c7 80:c5-c5 | +| test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | +| test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | +| test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql new file mode 100644 index 000000000000..edd5ae2c63bf --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql @@ -0,0 +1,12 @@ +import cpp +import semmle.code.cpp.valuenumbering.HashCons + +from HC h +where strictcount(h.getAnExpr()) > 1 +select + h, + strictconcat(Location loc + | loc = h.getAnExpr().getLocation() + | loc.getStartLine() + + ":c" + loc.getStartColumn() + "-c" + loc.getEndColumn() + , " ") diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql new file mode 100644 index 000000000000..f0ee6d237d32 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql @@ -0,0 +1,8 @@ +import cpp +import semmle.code.cpp.valuenumbering.HashCons + +// Every expression should have exactly one GVN. +// So this query should have zero results. +from Expr e +where count(hashCons(e)) != 1 +select e, concat(HC h | h = hashCons(e) | h.getKind(), ", ") diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp new file mode 100644 index 000000000000..45089be9feff --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -0,0 +1,100 @@ +int test00(int p0, int p1) { + int x, y; + unsigned char b; + + x = p0 + p1; + x = p0 + p1; // Same value as previous line. Should the assignment also be matched? + y = x; +} + +int global01 = 1; + +int test01(int p0, int p1) { + int x, y; + unsigned char b; + + x = p0 + p1 + global01; + x = p0 + p1 + global01; // Same structure as previous line. + y = x; // x is the same as x above +} + +int global02 = 2; + +void change_global02(); // Just a declaration + +int test02(int p0, int p1) { + int x, y; + unsigned char b; + + x = p0 + p1 + global02; + change_global02(); + x = p0 + p1 + global02; // same HashCons as above + y = x; +} + +int global03 = 3; + +void change_global03(); // Just a declaration + +int test03(int p0, int p1, int* p2) { + int x, y; + unsigned char b; + + x = p0 + p1 + global03; + *p2 = 0; + x = p0 + p1 + global03; // same HashCons as 43 + y = x; +} + +unsigned int my_strspn(const char *str, const char *chars) { + const char *ptr; + unsigned int result = 0; + + while (*str != '\0') { + // check *str against chars + ptr = chars; + while ((*ptr != *str) && (*ptr != '\0')) {ptr++;} + + // update + if (*ptr == '\0') { // ptr same as ptr on lines 53 and 56 + break; + } + result++; + } + + return result; // result same as result on line 62 +} + +int getAValue(); + +struct two_values { + signed short val1; + signed short val2; +}; + +void test04(two_values *vals) +{ + signed short v = getAValue(); // should this match getAValue() on line 80? + + if (v < vals->val1 + vals->val2) { + v = getAValue(); // should this match getAValue() on line 77? + } +} + +void test05(int x, int y, void *p) +{ + int v; + + v = p != 0 ? x : y; +} + +int regression_test00() { + int x = x = 10; + return x; +} + +void test06(int x) { + x++; + x++; // x is matched but x++ is not matched? +} + From 8b8ec7c5aa892f6856c4d3672f45a6a0951d8976 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Aug 2018 15:30:58 -0700 Subject: [PATCH 010/110] C++: add literal tests --- .../HashCons/GlobalValueNumbering.expected | 16 ++++++++++++++++ .../valuenumbering/HashCons/test.cpp | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected index 7807cc607ad0..8a350ba2f279 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected @@ -33,3 +33,19 @@ | test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 | +| test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 | +| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 35:c16-c16 | +| test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | +| test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | +| test.cpp:110:15:110:17 | array to pointer conversion | 110:c15-c17 111:c9-c11 | +| test.cpp:111:3:111:5 | str | 111:c3-c5 112:c3-c5 113:c3-c5 | +| test.cpp:112:9:112:11 | 2 | 112:c9-c11 113:c9-c11 | +| test.cpp:112:9:112:11 | (char *)... | 112:c9-c11 113:c9-c11 | +| test.cpp:112:9:112:11 | array to pointer conversion | 112:c9-c11 113:c9-c11 | +| test.cpp:115:13:115:15 | 0.0 | 115:c13-c15 116:c7-c9 | +| test.cpp:115:13:115:15 | (float)... | 115:c13-c15 116:c7-c9 | +| test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 | +| test.cpp:117:7:117:9 | 0.10000000000000001 | 117:c7-c9 118:c7-c9 | +| test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 45089be9feff..d672f66e8334 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -98,3 +98,22 @@ void test06(int x) { x++; // x is matched but x++ is not matched? } +// literals +void test07() { + int x = 1; + x = 1; + x = 2; + x = 2; + x = 1 + 2; + x = 1 + 2; + + char *str = "1"; + str = "1"; + str = "2"; + str = "2"; + + float y = 0.0; + y = 0.0; + y = 0.1; + y = 0.1; +} From d8dc75abf4086ca0ae6dd70ef83d3c4a939249cd Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Aug 2018 15:32:46 -0700 Subject: [PATCH 011/110] C++: rename HashCons test --- .../HashCons/{GlobalValueNumbering.expected => HashCons.expected} | 0 .../HashCons/{GlobalValueNumbering.ql => HashCons.ql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/test/library-tests/valuenumbering/HashCons/{GlobalValueNumbering.expected => HashCons.expected} (100%) rename cpp/ql/test/library-tests/valuenumbering/HashCons/{GlobalValueNumbering.ql => HashCons.ql} (100%) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected similarity index 100% rename from cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.expected rename to cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql similarity index 100% rename from cpp/ql/test/library-tests/valuenumbering/HashCons/GlobalValueNumbering.ql rename to cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql From cf222c51ac33a798beeb0617e3bc0614f702020c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Aug 2018 16:47:01 -0700 Subject: [PATCH 012/110] C++: treat constant-valued exprs structurally --- .../code/cpp/valuenumbering/HashCons.qll | 98 ++++++++++--------- .../valuenumbering/HashCons/HashCons.expected | 5 +- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 3bfc8654ae15..9c468178ee94 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -41,9 +41,11 @@ import cpp /** Used to represent the hash-cons of an expression. */ private cached newtype HCBase = - HC_IntConst(int val, Type t) { mk_IntConst(val,t,_) } + HC_IntLiteral(int val, Type t) { mk_IntLiteral(val,t,_) } or - HC_FloatConst(float val, Type t) { mk_FloatConst(val,t,_) } + HC_FloatLiteral(float val, Type t) { mk_FloatLiteral(val,t,_) } + or + HC_StringLiteral(string val, Type t) {mk_StringLiteral(val,t,_)} or HC_Variable(Variable x) { mk_Variable(x, _) @@ -106,8 +108,9 @@ class HC extends HCBase { /** Gets the kind of the HC. This can be useful for debugging. */ string getKind() { - if this instanceof HC_IntConst then result = "IntConst" else - if this instanceof HC_FloatConst then result = "FloatConst" else + if this instanceof HC_IntLiteral then result = "IntLiteral" else + if this instanceof HC_FloatLiteral then result = "FloatLiteral" else + if this instanceof HC_StringLiteral then result = "StringLiteral" else if this instanceof HC_Variable then result = "Variable" else if this instanceof HC_FieldAccess then result = "FieldAccess" else if this instanceof HC_Deref then result = "Deref" else @@ -144,33 +147,45 @@ class HC extends HCBase { } } -private predicate analyzableIntConst(Expr e) { +private predicate analyzableIntLiteral(Literal e) { strictcount (e.getValue().toInt()) = 1 and strictcount (e.getType().getUnspecifiedType()) = 1 } -private predicate mk_IntConst(int val, Type t, Expr e) { - analyzableIntConst(e) and +private predicate mk_IntLiteral(int val, Type t, Expr e) { + analyzableIntLiteral(e) and val = e.getValue().toInt() and - t = e.getType().getUnspecifiedType() + t = e.getType().getUnspecifiedType() and + t instanceof IntegralType } - -private predicate analyzableFloatConst(Expr e) { +private predicate analyzableFloatLiteral(Literal e) { strictcount (e.getValue().toFloat()) = 1 and - strictcount (e.getType().getUnspecifiedType()) = 1 and - not analyzableIntConst(e) + strictcount (e.getType().getUnspecifiedType()) = 1 } -private predicate mk_FloatConst(float val, Type t, Expr e) { - analyzableFloatConst(e) and +private predicate mk_FloatLiteral(float val, Type t, Expr e) { + analyzableFloatLiteral(e) and val = e.getValue().toFloat() and - t = e.getType().getUnspecifiedType() + t = e.getType().getUnspecifiedType() and + t instanceof FloatingPointType +} + +private predicate analyzableStringLiteral(Literal e) { + strictcount(e.getValue()) = 1 and + strictcount(e.getType().getUnspecifiedType()) = 1 +} + +private predicate mk_StringLiteral(string val, Type t, Expr e) { + analyzableStringLiteral(e) and + val = e.getValue() and + t = e.getType().getUnspecifiedType() and + t.(ArrayType).getBaseType() instanceof CharType + } private predicate analyzableDotFieldAccess(DotFieldAccess access) { strictcount (access.getTarget()) = 1 and - strictcount (access.getQualifier().getFullyConverted()) = 1 and - not analyzableConst(access) + strictcount (access.getQualifier().getFullyConverted()) = 1 } private predicate mk_DotFieldAccess( @@ -182,8 +197,7 @@ private predicate mk_DotFieldAccess( private predicate analyzablePointerFieldAccess(PointerFieldAccess access) { strictcount (access.getTarget()) = 1 and - strictcount (access.getQualifier().getFullyConverted()) = 1 and - not analyzableConst(access) + strictcount (access.getQualifier().getFullyConverted()) = 1 } private predicate mk_PointerFieldAccess( @@ -208,8 +222,7 @@ private predicate mk_PointerFieldAccess_with_deref( private predicate analyzableImplicitThisFieldAccess( ImplicitThisFieldAccess access) { strictcount (access.getTarget()) = 1 and - strictcount (access.getEnclosingFunction()) = 1 and - not analyzableConst(access) + strictcount (access.getEnclosingFunction()) = 1 } private predicate mk_ImplicitThisFieldAccess( @@ -238,8 +251,7 @@ private predicate mk_ImplicitThisFieldAccess_with_deref( private predicate analyzableVariable(VariableAccess access) { not (access instanceof FieldAccess) and - strictcount (access.getTarget()) = 1 and - not analyzableConst(access) + strictcount (access.getTarget()) = 1 } private predicate mk_Variable(Variable x, VariableAccess access) { @@ -249,8 +261,7 @@ private predicate mk_Variable(Variable x, VariableAccess access) { private predicate analyzableConversion(Conversion conv) { strictcount (conv.getType().getUnspecifiedType()) = 1 and - strictcount (conv.getExpr()) = 1 and - not analyzableConst(conv) + strictcount (conv.getExpr()) = 1 } private predicate mk_Conversion(Type t, HC child, Conversion conv) { @@ -263,8 +274,7 @@ private predicate analyzableBinaryOp(BinaryOperation op) { op.isPure() and strictcount (op.getLeftOperand().getFullyConverted()) = 1 and strictcount (op.getRightOperand().getFullyConverted()) = 1 and - strictcount (op.getOperator()) = 1 and - not analyzableConst(op) + strictcount (op.getOperator()) = 1 } private predicate mk_BinaryOp( @@ -279,8 +289,7 @@ private predicate analyzableUnaryOp(UnaryOperation op) { not (op instanceof PointerDereferenceExpr) and op.isPure() and strictcount (op.getOperand().getFullyConverted()) = 1 and - strictcount (op.getOperator()) = 1 and - not analyzableConst(op) + strictcount (op.getOperator()) = 1 } private predicate mk_UnaryOp(HC child, string opname, UnaryOperation op) { @@ -290,8 +299,7 @@ private predicate mk_UnaryOp(HC child, string opname, UnaryOperation op) { } private predicate analyzableThisExpr(ThisExpr thisExpr) { - strictcount(thisExpr.getEnclosingFunction()) = 1 and - not analyzableConst(thisExpr) + strictcount(thisExpr.getEnclosingFunction()) = 1 } private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) { @@ -301,8 +309,7 @@ private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) { private predicate analyzableArrayAccess(ArrayExpr ae) { strictcount (ae.getArrayBase().getFullyConverted()) = 1 and - strictcount (ae.getArrayOffset().getFullyConverted()) = 1 and - not analyzableConst(ae) + strictcount (ae.getArrayOffset().getFullyConverted()) = 1 } private predicate mk_ArrayAccess( @@ -314,8 +321,7 @@ private predicate mk_ArrayAccess( private predicate analyzablePointerDereferenceExpr( PointerDereferenceExpr deref) { - strictcount (deref.getOperand().getFullyConverted()) = 1 and - not analyzableConst(deref) + strictcount (deref.getOperand().getFullyConverted()) = 1 } private predicate mk_Deref( @@ -327,12 +333,16 @@ private predicate mk_Deref( /** Gets the hash-cons of expression `e`. */ cached HC hashCons(Expr e) { exists (int val, Type t - | mk_IntConst(val, t, e) and - result = HC_IntConst(val, t)) + | mk_IntLiteral(val, t, e) and + result = HC_IntLiteral(val, t)) or exists (float val, Type t - | mk_FloatConst(val, t, e) and - result = HC_FloatConst(val, t)) + | mk_FloatLiteral(val, t, e) and + result = HC_FloatLiteral(val, t)) + or + exists (string val, Type t + | mk_StringLiteral(val, t, e) and + result = HC_StringLiteral(val, t)) or // Variable with no SSA information. exists (Variable x @@ -377,12 +387,6 @@ cached HC hashCons(Expr e) { or (not analyzableExpr(e,_) and result = HC_Unanalyzable(e)) } - -private predicate analyzableConst(Expr e) { - analyzableIntConst(e) or - analyzableFloatConst(e) -} - /** * Holds if the expression is explicitly handled by `hashCons`. * Unanalyzable expressions still need to be given a hash-cons, @@ -390,7 +394,9 @@ private predicate analyzableConst(Expr e) { * expression. */ predicate analyzableExpr(Expr e, string kind) { - (analyzableConst(e) and kind = "Const") or + (analyzableIntLiteral(e) and kind = "IntLiteral") or + (analyzableFloatLiteral(e) and kind = "FloatLiteral") or + (analyzableStringLiteral(e) and kind = "StringLiteral") or (analyzableDotFieldAccess(e) and kind = "DotFieldAccess") or (analyzablePointerFieldAccess(e) and kind = "PointerFieldAccess") or (analyzableImplicitThisFieldAccess(e) and kind = "ImplicitThisFieldAccess") or diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 8a350ba2f279..38f7b07c0034 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -20,11 +20,12 @@ | test.cpp:43:7:43:24 | ... + ... | 43:c7-c24 45:c7-c24 | | test.cpp:43:12:43:13 | p1 | 43:c12-c13 45:c12-c13 | | test.cpp:43:17:43:24 | global03 | 43:c17-c24 45:c17-c24 | -| test.cpp:44:9:44:9 | 0 | 44:c9-c9 51:c25-c25 53:c18-c21 56:c39-c42 59:c17-c20 88:c12-c12 | +| test.cpp:44:9:44:9 | 0 | 44:c9-c9 51:c25-c25 88:c12-c12 | | test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 | | test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 | | test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 | | test.cpp:53:18:53:21 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 | +| test.cpp:53:18:53:21 | (int)... | 53:c18-c21 56:c39-c42 59:c17-c20 | | test.cpp:55:5:55:7 | ptr | 55:c5-c7 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 | | test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 | | test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 | @@ -36,7 +37,7 @@ | test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | | test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 | -| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 35:c16-c16 | +| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | array to pointer conversion | 110:c15-c17 111:c9-c11 | From a8895f4bed89e2bc659ad5e02f4b6e2178aa1454 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 24 Aug 2018 11:38:59 -0700 Subject: [PATCH 013/110] C++: Support crement ops in HashCons --- cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll | 1 - .../library-tests/valuenumbering/HashCons/HashCons.expected | 1 + cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 9c468178ee94..2e3e88382ba0 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -287,7 +287,6 @@ private predicate mk_BinaryOp( private predicate analyzableUnaryOp(UnaryOperation op) { not (op instanceof PointerDereferenceExpr) and - op.isPure() and strictcount (op.getOperand().getFullyConverted()) = 1 and strictcount (op.getOperator()) = 1 } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 38f7b07c0034..ca55cb9adeec 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -34,6 +34,7 @@ | test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | +| test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | | test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | | test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index d672f66e8334..ecfa543d5c15 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -95,7 +95,7 @@ int regression_test00() { void test06(int x) { x++; - x++; // x is matched but x++ is not matched? + x++; // x++ is matched } // literals From b8bd285d640d307924fb90e41ef843e25a7d8b50 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 24 Aug 2018 16:35:00 -0700 Subject: [PATCH 014/110] C++: support functions in HashCons --- .../code/cpp/valuenumbering/HashCons.qll | 142 +++++++++++++++++- .../valuenumbering/HashCons/HashCons.expected | 12 ++ .../valuenumbering/HashCons/Uniqueness.ql | 2 +- .../valuenumbering/HashCons/test.cpp | 28 ++++ 4 files changed, 182 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 2e3e88382ba0..d558f8381361 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -80,10 +80,25 @@ private cached newtype HCBase = mk_ArrayAccess(x,i,_) } or + HC_NonmemberFunctionCall(Function fcn, HC_Args args) { + mk_NonmemberFunctionCall(fcn, args, _) + } + or + HC_MemberFunctionCall(Function trg, HC qual, HC_Args args) { + mk_MemberFunctionCall(trg, qual, args, _) + } + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } +private cached newtype HC_Args = + HC_EmptyArgs(Function fcn) { + any() + } + or HC_ArgCons(Function fcn, HC hc, int i, HC_Args list) { + mk_ArgCons(fcn, hc, i, list, _) + } /** * HC is the hash-cons of an expression. The relationship between `Expr` * and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple @@ -120,6 +135,8 @@ class HC extends HCBase { if this instanceof HC_UnaryOp then result = "UnaryOp" else if this instanceof HC_ArrayAccess then result = "ArrayAccess" else if this instanceof HC_Unanalyzable then result = "Unanalyzable" else + if this instanceof HC_NonmemberFunctionCall then result = "NonmemberFunctionCall" else + if this instanceof HC_MemberFunctionCall then result = "MemberFunctionCall" else result = "error" } @@ -329,6 +346,116 @@ private predicate mk_Deref( p = hashCons(deref.getOperand().getFullyConverted()) } +private predicate analyzableNonmemberFunctionCall( + FunctionCall fc) { + forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and + strictcount(fc.getTarget()) = 1 and + not fc.getTarget().isMember() +} + +private predicate mk_NonmemberFunctionCall( + Function fcn, + HC_Args args, + FunctionCall fc +) { + fc.getTarget() = fcn and + analyzableNonmemberFunctionCall(fc) and + ( + exists(HC head, HC_Args tail | + args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + ) + or + fc.getNumberOfArguments() = 0 and + args = HC_EmptyArgs(fcn) + ) +} + +private predicate analyzableMemberFunctionCall( + FunctionCall fc) { + forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and + strictcount(fc.getTarget()) = 1 and + strictcount(fc.getQualifier()) = 1 +} + +private predicate analyzableFunctionCall( + FunctionCall fc +) { + analyzableNonmemberFunctionCall(fc) + or + analyzableMemberFunctionCall(fc) +} + +private predicate mk_MemberFunctionCall( + Function fcn, + HC qual, + HC_Args args, + FunctionCall fc +) { + fc.getTarget() = fcn and + analyzableMemberFunctionCall(fc) and + hashCons(fc.getQualifier()) = qual and + ( + exists(HC head, HC_Args tail | + args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + ) + or + fc.getNumberOfArguments() = 0 and + args = HC_EmptyArgs(fcn) + ) +} + +/* +private predicate analyzableImplicitThisFunctionCall(FunctionCall fc) { + forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and + strictcount(fc.getTarget()) = 1 and + fc.getQualifier().(ThisExpr).isCompilerGenerated() and + fc.getTarget().isMember() +} + +private predicate mk_ImplicitThisFunctionCall(Function fcn, Function targ, HC_Args args, FunctionCall fc) { + analyzableImplicitThisFunctionCall(fc) and + fc.getTarget() = targ and + fc.getEnclosingFunction() = fcn and + analyzableImplicitThisFunctionCall(fc) and + ( + exists(HC head, HC_Args tail | + args = HC_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail, fc) + ) + or + fc.getNumberOfArguments() = 0 and + args = HC_EmptyArgs(targ) + ) +} + +private predicate mk_ImplicitThisFunctionCall_with_qualifier( + Function fcn, + Function targ, + HC qual, + HC_Args args, + FunctionCall fc) { + mk_ImplicitThisFunctionCall(fcn, targ, args, fc) and + qual = HC_ThisExpr(fcn) +} +*/ +private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionCall fc) { + analyzableFunctionCall(fc) and + fc.getTarget() = fcn and + hc = hashCons(fc.getArgument(i).getFullyConverted()) and + ( + exists(HC head, HC_Args tail | + list = HC_ArgCons(fcn, head, i - 1, tail) and + mk_ArgCons(fcn, head, i - 1, tail, fc) and + i > 0 + ) + or + i = 0 and + list = HC_EmptyArgs(fcn) + ) +} + /** Gets the hash-cons of expression `e`. */ cached HC hashCons(Expr e) { exists (int val, Type t @@ -383,6 +510,17 @@ cached HC hashCons(Expr e) { exists (HC p | mk_Deref(p, e) and result = HC_Deref(p)) + or + exists(Function fcn, HC_Args args + | mk_NonmemberFunctionCall(fcn, args, e) and + result = HC_NonmemberFunctionCall(fcn, args) + ) + or + exists(Function fcn, HC qual, HC_Args args + | mk_MemberFunctionCall(fcn, qual, args, e) and + result = HC_MemberFunctionCall(fcn, qual, args) + ) + or (not analyzableExpr(e,_) and result = HC_Unanalyzable(e)) } @@ -405,5 +543,7 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableUnaryOp(e) and kind = "UnaryOp") or (analyzableThisExpr(e) and kind = "ThisExpr") or (analyzableArrayAccess(e) and kind = "ArrayAccess") or - (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") + (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or + (analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or + (analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index ca55cb9adeec..621dc12a74fb 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -1,3 +1,4 @@ +| file://:0:0:0:0 | this | 0:c0-c0 141:c23-c26 | | test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 7:c7-c7 | | test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 | | test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 | @@ -30,6 +31,8 @@ | test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 | | test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 | | test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 | +| test.cpp:77:20:77:28 | call to getAValue | 77:c20-c28 80:c9-c17 | +| test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 80:c9-c19 | | test.cpp:79:7:79:7 | v | 79:c7-c7 80:c5-c5 | | test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | @@ -51,3 +54,12 @@ | test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 | | test.cpp:117:7:117:9 | 0.10000000000000001 | 117:c7-c9 118:c7-c9 | | test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 | +| test.cpp:122:3:122:8 | call to test07 | 122:c3-c8 123:c3-c8 | +| test.cpp:125:3:125:11 | call to my_strspn | 125:c3-c11 126:c3-c11 | +| test.cpp:125:13:125:17 | array to pointer conversion | 125:c13-c17 126:c13-c17 129:c20-c24 | +| test.cpp:125:13:125:17 | foo | 125:c13-c17 126:c13-c17 129:c20-c24 | +| test.cpp:125:20:125:24 | array to pointer conversion | 125:c20-c24 126:c20-c24 129:c13-c17 | +| test.cpp:125:20:125:24 | bar | 125:c20-c24 126:c20-c24 129:c13-c17 | +| test.cpp:141:12:141:17 | call to getInt | 141:c12-c17 141:c29-c34 | +| test.cpp:146:10:146:11 | ih | 146:c10-c11 146:c31-c32 | +| test.cpp:146:13:146:25 | call to getDoubledInt | 146:c13-c25 146:c34-c46 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql index f0ee6d237d32..084c2b1b0814 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql @@ -1,7 +1,7 @@ import cpp import semmle.code.cpp.valuenumbering.HashCons -// Every expression should have exactly one GVN. +// Every expression should have exactly one HC. // So this query should have zero results. from Expr e where count(hashCons(e)) != 1 diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index ecfa543d5c15..36e31444af20 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -117,3 +117,31 @@ void test07() { y = 0.1; y = 0.1; } + +void test08() { + test07(); + test07(); + + my_strspn("foo", "bar"); + my_strspn("foo", "bar"); + + + my_strspn("bar", "foo"); +} + +class IntHolder { + int myInt; + + int getInt() { + return myInt; + } + +public: + int getDoubledInt() { + return getInt() + this->getInt(); + } +}; + +int quadrupleInt(IntHolder ih) { + return ih.getDoubledInt() + ih.getDoubledInt(); +} From 77c5a8e7bfc7bc93f93975e422421fc05847016e Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 24 Aug 2018 17:15:28 -0700 Subject: [PATCH 015/110] C++: support impure binary operations in HashCons --- cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll | 1 - .../valuenumbering/HashCons/HashCons.expected | 3 +++ .../library-tests/valuenumbering/HashCons/test.cpp | 10 ++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index d558f8381361..5d8efccea28f 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -288,7 +288,6 @@ private predicate mk_Conversion(Type t, HC child, Conversion conv) { } private predicate analyzableBinaryOp(BinaryOperation op) { - op.isPure() and strictcount (op.getLeftOperand().getFullyConverted()) = 1 and strictcount (op.getRightOperand().getFullyConverted()) = 1 and strictcount (op.getOperator()) = 1 diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 621dc12a74fb..3998c67bbac5 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -63,3 +63,6 @@ | test.cpp:141:12:141:17 | call to getInt | 141:c12-c17 141:c29-c34 | | test.cpp:146:10:146:11 | ih | 146:c10-c11 146:c31-c32 | | test.cpp:146:13:146:25 | call to getDoubledInt | 146:c13-c25 146:c34-c46 | +| test.cpp:150:3:150:3 | x | 150:c3-c3 150:c9-c9 151:c3-c3 151:c9-c9 152:c12-c12 | +| test.cpp:150:3:150:5 | ... ++ | 150:c3-c5 150:c9-c11 151:c3-c5 151:c9-c11 152:c10-c12 | +| test.cpp:150:3:150:11 | ... + ... | 150:c3-c11 151:c3-c11 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 36e31444af20..20e249d72a41 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -138,10 +138,16 @@ class IntHolder { public: int getDoubledInt() { - return getInt() + this->getInt(); + return getInt() + this->getInt(); // getInt() and this->getInt() should be the same } }; -int quadrupleInt(IntHolder ih) { +int test09(IntHolder ih) { return ih.getDoubledInt() + ih.getDoubledInt(); } + +int test10(int x) { + x++ + x++; + x++ + x++; // same as above + return ++x; // ++x is not the same as x++ +} From e0af30a789027f94609bf558dd29a6f6e23879d6 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 24 Aug 2018 17:42:30 -0700 Subject: [PATCH 016/110] C++: clean up commented-out code --- .../code/cpp/valuenumbering/HashCons.qll | 51 ++++--------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 5d8efccea28f..f6eb700ce5f6 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -92,6 +92,7 @@ private cached newtype HCBase = // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } +/** Used to implement hash-consing of argument lists */ private cached newtype HC_Args = HC_EmptyArgs(Function fcn) { any() @@ -377,14 +378,6 @@ private predicate analyzableMemberFunctionCall( strictcount(fc.getQualifier()) = 1 } -private predicate analyzableFunctionCall( - FunctionCall fc -) { - analyzableNonmemberFunctionCall(fc) - or - analyzableMemberFunctionCall(fc) -} - private predicate mk_MemberFunctionCall( Function fcn, HC qual, @@ -405,40 +398,18 @@ private predicate mk_MemberFunctionCall( ) } -/* -private predicate analyzableImplicitThisFunctionCall(FunctionCall fc) { - forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and - strictcount(fc.getTarget()) = 1 and - fc.getQualifier().(ThisExpr).isCompilerGenerated() and - fc.getTarget().isMember() -} - -private predicate mk_ImplicitThisFunctionCall(Function fcn, Function targ, HC_Args args, FunctionCall fc) { - analyzableImplicitThisFunctionCall(fc) and - fc.getTarget() = targ and - fc.getEnclosingFunction() = fcn and - analyzableImplicitThisFunctionCall(fc) and - ( - exists(HC head, HC_Args tail | - args = HC_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail, fc) - ) - or - fc.getNumberOfArguments() = 0 and - args = HC_EmptyArgs(targ) - ) +private predicate analyzableFunctionCall( + FunctionCall fc +) { + analyzableNonmemberFunctionCall(fc) + or + analyzableMemberFunctionCall(fc) } -private predicate mk_ImplicitThisFunctionCall_with_qualifier( - Function fcn, - Function targ, - HC qual, - HC_Args args, - FunctionCall fc) { - mk_ImplicitThisFunctionCall(fcn, targ, args, fc) and - qual = HC_ThisExpr(fcn) -} -*/ +/** + * Holds if `fc` is a call to `fcn`, `fc`'s first `i-1` arguments have hash-cons + * `list`, and `fc`'s `i`th argument has hash-cons `hc` + */ private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionCall fc) { analyzableFunctionCall(fc) and fc.getTarget() = fcn and From 3a5eb03055eaea3295703421f7250cb3ebdb3ec8 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 27 Aug 2018 10:05:09 -0700 Subject: [PATCH 017/110] C++: change floating point value in test --- .../library-tests/valuenumbering/HashCons/HashCons.expected | 2 +- cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 3998c67bbac5..6cc1b8589e53 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -52,7 +52,7 @@ | test.cpp:115:13:115:15 | 0.0 | 115:c13-c15 116:c7-c9 | | test.cpp:115:13:115:15 | (float)... | 115:c13-c15 116:c7-c9 | | test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 | -| test.cpp:117:7:117:9 | 0.10000000000000001 | 117:c7-c9 118:c7-c9 | +| test.cpp:117:7:117:9 | 0.5 | 117:c7-c9 118:c7-c9 | | test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 | | test.cpp:122:3:122:8 | call to test07 | 122:c3-c8 123:c3-c8 | | test.cpp:125:3:125:11 | call to my_strspn | 125:c3-c11 126:c3-c11 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 20e249d72a41..f073654c38bd 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -114,8 +114,8 @@ void test07() { float y = 0.0; y = 0.0; - y = 0.1; - y = 0.1; + y = 0.5; + y = 0.5; } void test08() { From 91da02bacfc2a02ead34ba5e2a64f434049ac309 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 27 Aug 2018 11:38:16 -0700 Subject: [PATCH 018/110] C++: uniqueness fixes for HashCons --- .../code/cpp/valuenumbering/HashCons.qll | 44 ++++++++++++++----- .../valuenumbering/HashCons/HashCons.expected | 1 + .../valuenumbering/HashCons/test.cpp | 5 +++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index f6eb700ce5f6..fb366b079faa 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -47,6 +47,8 @@ private cached newtype HCBase = or HC_StringLiteral(string val, Type t) {mk_StringLiteral(val,t,_)} or + HC_Nullptr() {mk_Nullptr(_)} + or HC_Variable(Variable x) { mk_Variable(x, _) } @@ -167,30 +169,42 @@ class HC extends HCBase { private predicate analyzableIntLiteral(Literal e) { strictcount (e.getValue().toInt()) = 1 and - strictcount (e.getType().getUnspecifiedType()) = 1 + strictcount (e.getType().getUnspecifiedType()) = 1 and + e.getType().getUnspecifiedType() instanceof IntegralType } private predicate mk_IntLiteral(int val, Type t, Expr e) { analyzableIntLiteral(e) and val = e.getValue().toInt() and - t = e.getType().getUnspecifiedType() and - t instanceof IntegralType + t = e.getType().getUnspecifiedType() } + + private predicate analyzableFloatLiteral(Literal e) { strictcount (e.getValue().toFloat()) = 1 and - strictcount (e.getType().getUnspecifiedType()) = 1 + strictcount (e.getType().getUnspecifiedType()) = 1 and + e.getType().getUnspecifiedType() instanceof FloatingPointType } private predicate mk_FloatLiteral(float val, Type t, Expr e) { analyzableFloatLiteral(e) and val = e.getValue().toFloat() and - t = e.getType().getUnspecifiedType() and - t instanceof FloatingPointType + t = e.getType().getUnspecifiedType() +} + +private predicate analyzableNullptr(NullValue e) { + strictcount (e.getType().getUnspecifiedType()) = 1 and + e.getType() instanceof NullPointerType +} + +private predicate mk_Nullptr(Expr e) { + analyzableNullptr(e) } private predicate analyzableStringLiteral(Literal e) { strictcount(e.getValue()) = 1 and - strictcount(e.getType().getUnspecifiedType()) = 1 + strictcount(e.getType().getUnspecifiedType()) = 1 and + e.getType().getUnspecifiedType().(ArrayType).getBaseType() instanceof CharType } private predicate mk_StringLiteral(string val, Type t, Expr e) { @@ -348,9 +362,9 @@ private predicate mk_Deref( private predicate analyzableNonmemberFunctionCall( FunctionCall fc) { - forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and + forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1) and strictcount(fc.getTarget()) = 1 and - not fc.getTarget().isMember() + not exists(fc.getQualifier()) } private predicate mk_NonmemberFunctionCall( @@ -373,9 +387,9 @@ private predicate mk_NonmemberFunctionCall( private predicate analyzableMemberFunctionCall( FunctionCall fc) { - forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and + forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1) and strictcount(fc.getTarget()) = 1 and - strictcount(fc.getQualifier()) = 1 + strictcount(fc.getQualifier().getFullyConverted()) = 1 } private predicate mk_MemberFunctionCall( @@ -386,7 +400,7 @@ private predicate mk_MemberFunctionCall( ) { fc.getTarget() = fcn and analyzableMemberFunctionCall(fc) and - hashCons(fc.getQualifier()) = qual and + hashCons(fc.getQualifier().getFullyConverted()) = qual and ( exists(HC head, HC_Args tail | args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and @@ -490,6 +504,11 @@ cached HC hashCons(Expr e) { | mk_MemberFunctionCall(fcn, qual, args, e) and result = HC_MemberFunctionCall(fcn, qual, args) ) + or + ( + mk_Nullptr(e) and + result = HC_Nullptr() + ) or (not analyzableExpr(e,_) and result = HC_Unanalyzable(e)) @@ -504,6 +523,7 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableIntLiteral(e) and kind = "IntLiteral") or (analyzableFloatLiteral(e) and kind = "FloatLiteral") or (analyzableStringLiteral(e) and kind = "StringLiteral") or + (analyzableNullptr(e) and kind = "Nullptr") or (analyzableDotFieldAccess(e) and kind = "DotFieldAccess") or (analyzablePointerFieldAccess(e) and kind = "PointerFieldAccess") or (analyzableImplicitThisFieldAccess(e) and kind = "ImplicitThisFieldAccess") or diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 6cc1b8589e53..f921b24dc1ca 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -66,3 +66,4 @@ | test.cpp:150:3:150:3 | x | 150:c3-c3 150:c9-c9 151:c3-c3 151:c9-c9 152:c12-c12 | | test.cpp:150:3:150:5 | ... ++ | 150:c3-c5 150:c9-c11 151:c3-c5 151:c9-c11 152:c10-c12 | | test.cpp:150:3:150:11 | ... + ... | 150:c3-c11 151:c3-c11 | +| test.cpp:156:14:156:20 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index f073654c38bd..89661e2b6d77 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -151,3 +151,8 @@ int test10(int x) { x++ + x++; // same as above return ++x; // ++x is not the same as x++ } + +void* test11() { + nullptr == nullptr; + return nullptr; +} From e6314c5f35b947c1908a8ca690cc0d6822000bd7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 27 Aug 2018 11:48:37 -0700 Subject: [PATCH 019/110] C++: add support for enums in HashCons --- .../code/cpp/valuenumbering/HashCons.qll | 18 ++++++++++++++++++ .../valuenumbering/HashCons/HashCons.expected | 2 ++ .../valuenumbering/HashCons/test.cpp | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index fb366b079faa..0a8fd0c0c1f9 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -43,6 +43,8 @@ import cpp private cached newtype HCBase = HC_IntLiteral(int val, Type t) { mk_IntLiteral(val,t,_) } or + HC_EnumConstantAccess(EnumConstant val, Type t) { mk_EnumConstantAccess(val,t,_) } + or HC_FloatLiteral(float val, Type t) { mk_FloatLiteral(val,t,_) } or HC_StringLiteral(string val, Type t) {mk_StringLiteral(val,t,_)} @@ -179,6 +181,17 @@ private predicate mk_IntLiteral(int val, Type t, Expr e) { t = e.getType().getUnspecifiedType() } +private predicate analyzableEnumConstantAccess(EnumConstantAccess e) { + strictcount (e.getValue().toInt()) = 1 and + strictcount (e.getType().getUnspecifiedType()) = 1 and + e.getType().getUnspecifiedType() instanceof Enum +} + +private predicate mk_EnumConstantAccess(EnumConstant val, Type t, Expr e) { + analyzableEnumConstantAccess(e) and + val = e.(EnumConstantAccess).getTarget() and + t = e.getType().getUnspecifiedType() +} private predicate analyzableFloatLiteral(Literal e) { strictcount (e.getValue().toFloat()) = 1 and @@ -446,6 +459,10 @@ cached HC hashCons(Expr e) { | mk_IntLiteral(val, t, e) and result = HC_IntLiteral(val, t)) or + exists (EnumConstant val, Type t + | mk_EnumConstantAccess(val, t, e) and + result = HC_EnumConstantAccess(val, t)) + or exists (float val, Type t | mk_FloatLiteral(val, t, e) and result = HC_FloatLiteral(val, t)) @@ -521,6 +538,7 @@ cached HC hashCons(Expr e) { */ predicate analyzableExpr(Expr e, string kind) { (analyzableIntLiteral(e) and kind = "IntLiteral") or + (analyzableEnumConstantAccess(e) and kind = "EnumConstantAccess") or (analyzableFloatLiteral(e) and kind = "FloatLiteral") or (analyzableStringLiteral(e) and kind = "StringLiteral") or (analyzableNullptr(e) and kind = "Nullptr") or diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index f921b24dc1ca..d1a42db322d5 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -67,3 +67,5 @@ | test.cpp:150:3:150:5 | ... ++ | 150:c3-c5 150:c9-c11 151:c3-c5 151:c9-c11 152:c10-c12 | | test.cpp:150:3:150:11 | ... + ... | 150:c3-c11 151:c3-c11 | | test.cpp:156:14:156:20 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | +| test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 | +| test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 89661e2b6d77..e8fff46a5c91 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -156,3 +156,19 @@ void* test11() { nullptr == nullptr; return nullptr; } + +enum t1 { + e1x1 = 1, + e1x2 = 2 +}; + +enum t2 { + e2x1 = 1, + e2x2 = 2 +}; + +int test12() { + e1x1 == e2x1; + e1x1 == e2x2; + return e1x2; +} From fede8d63d4fed29924d79c0744a3c5e138ee4e7c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 27 Aug 2018 13:44:27 -0700 Subject: [PATCH 020/110] C++: respond to PR comments --- .../code/cpp/valuenumbering/HashCons.qll | 118 ++++++++---------- .../valuenumbering/HashCons/HashCons.expected | 6 +- .../valuenumbering/HashCons/HashCons.ql | 2 +- .../valuenumbering/HashCons/Uniqueness.ql | 2 +- .../valuenumbering/HashCons/test.cpp | 12 ++ 5 files changed, 74 insertions(+), 66 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 0a8fd0c0c1f9..3fe9242086ca 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -55,13 +55,13 @@ private cached newtype HCBase = mk_Variable(x, _) } or - HC_FieldAccess(HC s, Field f) { + HC_FieldAccess(HashCons s, Field f) { mk_DotFieldAccess(s,f,_) or mk_PointerFieldAccess_with_deref(s,f,_) or mk_ImplicitThisFieldAccess_with_deref(s,f,_) } or - HC_Deref(HC p) { + HC_Deref(HashCons p) { mk_Deref(p,_) or mk_PointerFieldAccess(p,_,_) or mk_ImplicitThisFieldAccess_with_qualifier(p,_,_) @@ -72,15 +72,15 @@ private cached newtype HCBase = mk_ImplicitThisFieldAccess(fcn,_,_) } or - HC_Conversion(Type t, HC child) { mk_Conversion(t, child, _) } + HC_Conversion(Type t, HashCons child) { mk_Conversion(t, child, _) } or - HC_BinaryOp(HC lhs, HC rhs, string opname) { + HC_BinaryOp(HashCons lhs, HashCons rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or - HC_UnaryOp(HC child, string opname) { mk_UnaryOp(child, opname, _) } + HC_UnaryOp(HashCons child, string opname) { mk_UnaryOp(child, opname, _) } or - HC_ArrayAccess(HC x, HC i) { + HC_ArrayAccess(HashCons x, HashCons i) { mk_ArrayAccess(x,i,_) } or @@ -88,7 +88,7 @@ private cached newtype HCBase = mk_NonmemberFunctionCall(fcn, args, _) } or - HC_MemberFunctionCall(Function trg, HC qual, HC_Args args) { + HC_MemberFunctionCall(Function trg, HashCons qual, HC_Args args) { mk_MemberFunctionCall(trg, qual, args, _) } or @@ -101,11 +101,11 @@ private cached newtype HC_Args = HC_EmptyArgs(Function fcn) { any() } - or HC_ArgCons(Function fcn, HC hc, int i, HC_Args list) { + or HC_ArgCons(Function fcn, HashCons hc, int i, HC_Args list) { mk_ArgCons(fcn, hc, i, list, _) } /** - * HC is the hash-cons of an expression. The relationship between `Expr` + * HashCons is the hash-cons of an expression. The relationship between `Expr` * and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple * expressions can have the same `HC`. If two expressions have the same * `HC`, it means that they are structurally equal. The `HC` is an opaque @@ -118,9 +118,7 @@ private cached newtype HC_Args = * expression with this `HC` and using its `toString` and `getLocation` * methods. */ -class HC extends HCBase { - HC() { this instanceof HCBase } - +class HashCons extends HCBase { /** Gets an expression that has this HC. */ Expr getAnExpr() { this = hashCons(result) @@ -234,7 +232,7 @@ private predicate analyzableDotFieldAccess(DotFieldAccess access) { } private predicate mk_DotFieldAccess( - HC qualifier, Field target, DotFieldAccess access) { + HashCons qualifier, Field target, DotFieldAccess access) { analyzableDotFieldAccess(access) and target = access.getTarget() and qualifier = hashCons(access.getQualifier().getFullyConverted()) @@ -246,9 +244,9 @@ private predicate analyzablePointerFieldAccess(PointerFieldAccess access) { } private predicate mk_PointerFieldAccess( - HC qualifier, Field target, + HashCons qualifier, Field target, PointerFieldAccess access) { - analyzablePointerFieldAccess(access) and + analyzablePointerFieldAccess(access) and target = access.getTarget() and qualifier = hashCons(access.getQualifier().getFullyConverted()) } @@ -257,38 +255,35 @@ private predicate mk_PointerFieldAccess( * `obj->field` is equivalent to `(*obj).field`, so we need to wrap an * extra `HC_Deref` around the qualifier. */ -private predicate mk_PointerFieldAccess_with_deref( - HC new_qualifier, Field target, PointerFieldAccess access) { - exists (HC qualifier +private predicate mk_PointerFieldAccess_with_deref (HashCons new_qualifier, Field target, + PointerFieldAccess access) { + exists (HashCons qualifier | mk_PointerFieldAccess(qualifier, target, access) and new_qualifier = HC_Deref(qualifier)) } -private predicate analyzableImplicitThisFieldAccess( - ImplicitThisFieldAccess access) { +private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) { strictcount (access.getTarget()) = 1 and strictcount (access.getEnclosingFunction()) = 1 } -private predicate mk_ImplicitThisFieldAccess( - Function fcn, Field target, +private predicate mk_ImplicitThisFieldAccess(Function fcn, Field target, ImplicitThisFieldAccess access) { analyzableImplicitThisFieldAccess(access) and target = access.getTarget() and fcn = access.getEnclosingFunction() } -private predicate mk_ImplicitThisFieldAccess_with_qualifier( - HC qualifier, Field target, +private predicate mk_ImplicitThisFieldAccess_with_qualifier( HashCons qualifier, Field target, ImplicitThisFieldAccess access) { exists (Function fcn | mk_ImplicitThisFieldAccess(fcn, target, access) and qualifier = HC_ThisExpr(fcn)) } -private predicate mk_ImplicitThisFieldAccess_with_deref( - HC new_qualifier, Field target, ImplicitThisFieldAccess access) { - exists (HC qualifier +private predicate mk_ImplicitThisFieldAccess_with_deref(HashCons new_qualifier, Field target, + ImplicitThisFieldAccess access) { + exists (HashCons qualifier | mk_ImplicitThisFieldAccess_with_qualifier( qualifier, target, access) and new_qualifier = HC_Deref(qualifier)) @@ -309,7 +304,7 @@ private predicate analyzableConversion(Conversion conv) { strictcount (conv.getExpr()) = 1 } -private predicate mk_Conversion(Type t, HC child, Conversion conv) { +private predicate mk_Conversion(Type t, HashCons child, Conversion conv) { analyzableConversion(conv) and t = conv.getType().getUnspecifiedType() and child = hashCons(conv.getExpr()) @@ -321,8 +316,7 @@ private predicate analyzableBinaryOp(BinaryOperation op) { strictcount (op.getOperator()) = 1 } -private predicate mk_BinaryOp( - HC lhs, HC rhs, string opname, BinaryOperation op) { +private predicate mk_BinaryOp(HashCons lhs, HashCons rhs, string opname, BinaryOperation op) { analyzableBinaryOp(op) and lhs = hashCons(op.getLeftOperand().getFullyConverted()) and rhs = hashCons(op.getRightOperand().getFullyConverted()) and @@ -335,7 +329,7 @@ private predicate analyzableUnaryOp(UnaryOperation op) { strictcount (op.getOperator()) = 1 } -private predicate mk_UnaryOp(HC child, string opname, UnaryOperation op) { +private predicate mk_UnaryOp(HashCons child, string opname, UnaryOperation op) { analyzableUnaryOp(op) and child = hashCons(op.getOperand().getFullyConverted()) and opname = op.getOperator() @@ -355,40 +349,36 @@ private predicate analyzableArrayAccess(ArrayExpr ae) { strictcount (ae.getArrayOffset().getFullyConverted()) = 1 } -private predicate mk_ArrayAccess( - HC base, HC offset, ArrayExpr ae) { +private predicate mk_ArrayAccess(HashCons base, HashCons offset, ArrayExpr ae) { analyzableArrayAccess(ae) and base = hashCons(ae.getArrayBase().getFullyConverted()) and offset = hashCons(ae.getArrayOffset().getFullyConverted()) } -private predicate analyzablePointerDereferenceExpr( - PointerDereferenceExpr deref) { +private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) { strictcount (deref.getOperand().getFullyConverted()) = 1 } -private predicate mk_Deref( - HC p, PointerDereferenceExpr deref) { +private predicate mk_Deref(HashCons p, PointerDereferenceExpr deref) { analyzablePointerDereferenceExpr(deref) and p = hashCons(deref.getOperand().getFullyConverted()) } -private predicate analyzableNonmemberFunctionCall( - FunctionCall fc) { - forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1) and +private predicate analyzableNonmemberFunctionCall(FunctionCall fc) { + forall(int i | + exists(fc.getArgument(i)) | + strictcount(fc.getArgument(i).getFullyConverted()) = 1 + ) and strictcount(fc.getTarget()) = 1 and not exists(fc.getQualifier()) } -private predicate mk_NonmemberFunctionCall( - Function fcn, - HC_Args args, - FunctionCall fc +private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionCall fc ) { fc.getTarget() = fcn and analyzableNonmemberFunctionCall(fc) and ( - exists(HC head, HC_Args tail | + exists(HashCons head, HC_Args tail | args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) ) @@ -400,14 +390,17 @@ private predicate mk_NonmemberFunctionCall( private predicate analyzableMemberFunctionCall( FunctionCall fc) { - forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1) and + forall(int i | + exists(fc.getArgument(i)) | + strictcount(fc.getArgument(i).getFullyConverted()) = 1 + ) and strictcount(fc.getTarget()) = 1 and strictcount(fc.getQualifier().getFullyConverted()) = 1 } private predicate mk_MemberFunctionCall( Function fcn, - HC qual, + HashCons qual, HC_Args args, FunctionCall fc ) { @@ -415,7 +408,7 @@ private predicate mk_MemberFunctionCall( analyzableMemberFunctionCall(fc) and hashCons(fc.getQualifier().getFullyConverted()) = qual and ( - exists(HC head, HC_Args tail | + exists(HashCons head, HC_Args tail | args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) ) @@ -434,15 +427,15 @@ private predicate analyzableFunctionCall( } /** - * Holds if `fc` is a call to `fcn`, `fc`'s first `i-1` arguments have hash-cons - * `list`, and `fc`'s `i`th argument has hash-cons `hc` + * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons + * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionCall fc) { +private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, FunctionCall fc) { analyzableFunctionCall(fc) and fc.getTarget() = fcn and hc = hashCons(fc.getArgument(i).getFullyConverted()) and ( - exists(HC head, HC_Args tail | + exists(HashCons head, HC_Args tail | list = HC_ArgCons(fcn, head, i - 1, tail) and mk_ArgCons(fcn, head, i - 1, tail, fc) and i > 0 @@ -454,7 +447,7 @@ private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionC } /** Gets the hash-cons of expression `e`. */ -cached HC hashCons(Expr e) { +cached HashCons hashCons(Expr e) { exists (int val, Type t | mk_IntLiteral(val, t, e) and result = HC_IntLiteral(val, t)) @@ -471,20 +464,19 @@ cached HC hashCons(Expr e) { | mk_StringLiteral(val, t, e) and result = HC_StringLiteral(val, t)) or - // Variable with no SSA information. exists (Variable x | mk_Variable(x, e) and result = HC_Variable(x)) or - exists (HC qualifier, Field target + exists (HashCons qualifier, Field target | mk_DotFieldAccess(qualifier, target, e) and result = HC_FieldAccess(qualifier, target)) or - exists (HC qualifier, Field target + exists (HashCons qualifier, Field target | mk_PointerFieldAccess_with_deref(qualifier, target, e) and result = HC_FieldAccess(qualifier, target)) or - exists (HC qualifier, Field target + exists (HashCons qualifier, Field target | mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and result = HC_FieldAccess(qualifier, target)) or @@ -492,23 +484,23 @@ cached HC hashCons(Expr e) { | mk_ThisExpr(fcn, e) and result = HC_ThisExpr(fcn)) or - exists (Type t, HC child + exists (Type t, HashCons child | mk_Conversion(t, child, e) and result = HC_Conversion(t, child)) or - exists (HC lhs, HC rhs, string opname + exists (HashCons lhs, HashCons rhs, string opname | mk_BinaryOp(lhs, rhs, opname, e) and result = HC_BinaryOp(lhs, rhs, opname)) or - exists (HC child, string opname + exists (HashCons child, string opname | mk_UnaryOp(child, opname, e) and result = HC_UnaryOp(child, opname)) or - exists (HC x, HC i + exists (HashCons x, HashCons i | mk_ArrayAccess(x, i, e) and result = HC_ArrayAccess(x, i)) or - exists (HC p + exists (HashCons p | mk_Deref(p, e) and result = HC_Deref(p)) or @@ -517,7 +509,7 @@ cached HC hashCons(Expr e) { result = HC_NonmemberFunctionCall(fcn, args) ) or - exists(Function fcn, HC qual, HC_Args args + exists(Function fcn, HashCons qual, HC_Args args | mk_MemberFunctionCall(fcn, qual, args, e) and result = HC_MemberFunctionCall(fcn, qual, args) ) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index d1a42db322d5..7f4a5d6ed383 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,7 +38,7 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | | test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | @@ -69,3 +69,7 @@ | test.cpp:156:14:156:20 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | | test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 | | test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 | +| test.cpp:179:10:179:22 | (...) | 179:c10-c22 179:c10-c22 | +| test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 | +| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 | +| test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql index edd5ae2c63bf..577e6ac62de1 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql @@ -1,7 +1,7 @@ import cpp import semmle.code.cpp.valuenumbering.HashCons -from HC h +from HashCons h where strictcount(h.getAnExpr()) > 1 select h, diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql index 084c2b1b0814..d953b53cd49e 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql @@ -5,4 +5,4 @@ import semmle.code.cpp.valuenumbering.HashCons // So this query should have zero results. from Expr e where count(hashCons(e)) != 1 -select e, concat(HC h | h = hashCons(e) | h.getKind(), ", ") +select e, concat(HashCons h | h = hashCons(e) | h.getKind(), ", ") diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index e8fff46a5c91..ed31f21d35aa 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -172,3 +172,15 @@ int test12() { e1x1 == e2x2; return e1x2; } + +#define SQUARE(x) ((x) * (x)) + +int test13(int y) { + return SQUARE(y + 1); +} + +#define SQUARE(x) x * x + +int test14(int y) { + return SQUARE(y); +} From 5549b6fcab04119f4b436b4f0c051478bb4253cf Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 28 Aug 2018 13:28:11 -0700 Subject: [PATCH 021/110] C++: HashCons for new, new[], sizeof, alignof --- .../code/cpp/valuenumbering/HashCons.qll | 274 +++++++++++++++++- .../valuenumbering/HashCons/HashCons.expected | 28 +- .../valuenumbering/HashCons/test.cpp | 87 ++++++ 3 files changed, 383 insertions(+), 6 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 3fe9242086ca..fedaabf47e55 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -2,9 +2,9 @@ * Provides an implementation of Hash consing. * See https://en.wikipedia.org/wiki/Hash_consing * - * The predicate `hashCons` converts an expression into a `HC`, which is an + * The predicate `hashCons` converts an expression into a `HashCons`, which is an * abstract type presenting the hash-cons of the expression. If two - * expressions have the same `HC` then they are structurally equal. + * expressions have the same `HashCons` then they are structurally equal. * * Important note: this library ignores the possibility that the value of * an expression might change between one occurrence and the next. For @@ -92,18 +92,51 @@ private cached newtype HCBase = mk_MemberFunctionCall(trg, qual, args, _) } or + HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init) { + mk_NewExpr(t, alloc, init, _, _) + } or + HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init) { + mk_NewArrayExpr(t, alloc, init, _, _) + } + or + HC_SizeofType(Type t) {mk_SizeofType(t, _)} + or + HC_SizeofExpr(HashCons child) {mk_SizeofExpr(child, _)} + or + HC_AlignofType(Type t) {mk_AlignofType(t, _)} + or + HC_AlignofExpr(HashCons child) {mk_AlignofExpr(child, _)} + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } +/** Used to implement hash-consing of `new` placement argument lists */ +private newtype HC_Alloc = + HC_EmptyAllocArgs(Function fcn) { + exists(NewOrNewArrayExpr n | + n.getAllocator() = fcn + ) + } + or HC_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned) { + mk_AllocArgCons(fcn, hc, i, list, aligned, _) + } + or + HC_NoAlloc() +private newtype HC_Init = + HC_NoInit() + or + HC_HasInit(HashCons hc) {mk_HasInit(hc, _)} + /** Used to implement hash-consing of argument lists */ -private cached newtype HC_Args = +private newtype HC_Args = HC_EmptyArgs(Function fcn) { any() } or HC_ArgCons(Function fcn, HashCons hc, int i, HC_Args list) { mk_ArgCons(fcn, hc, i, list, _) } + /** * HashCons is the hash-cons of an expression. The relationship between `Expr` * and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple @@ -127,8 +160,10 @@ class HashCons extends HCBase { /** Gets the kind of the HC. This can be useful for debugging. */ string getKind() { if this instanceof HC_IntLiteral then result = "IntLiteral" else + if this instanceof HC_EnumConstantAccess then result = "EnumConstantAccess" else if this instanceof HC_FloatLiteral then result = "FloatLiteral" else if this instanceof HC_StringLiteral then result = "StringLiteral" else + if this instanceof HC_Nullptr then result = "Nullptr" else if this instanceof HC_Variable then result = "Variable" else if this instanceof HC_FieldAccess then result = "FieldAccess" else if this instanceof HC_Deref then result = "Deref" else @@ -140,6 +175,12 @@ class HashCons extends HCBase { if this instanceof HC_Unanalyzable then result = "Unanalyzable" else if this instanceof HC_NonmemberFunctionCall then result = "NonmemberFunctionCall" else if this instanceof HC_MemberFunctionCall then result = "MemberFunctionCall" else + if this instanceof HC_NewExpr then result = "NewExpr" else + if this instanceof HC_NewArrayExpr then result = "NewArrayExpr" else + if this instanceof HC_SizeofType then result = "SizeofTypeOperator" else + if this instanceof HC_SizeofExpr then result = "SizeofExprOperator" else + if this instanceof HC_AlignofType then result = "AlignofTypeOperator" else + if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else result = "error" } @@ -446,6 +487,195 @@ private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, Fun ) } + +/** + * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons + * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. + */ +private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, FunctionCall fc) { + analyzableFunctionCall(fc) and + fc.getTarget() = fcn and + hc = hashCons(fc.getArgument(i).getFullyConverted()) and + ( + exists(HashCons head, HC_Alloc tail | + list = HC_AllocArgCons(fcn, head, i - 1, tail, aligned) and + mk_AllocArgCons(fcn, head, i - 1, tail, aligned, fc) and + ( + aligned = true and + i > 2 + or + aligned = false and + i > 1 + ) + ) + or + ( + aligned = true and + i = 2 + or + aligned = false and + i = 1 + ) and + list = HC_EmptyAllocArgs(fcn) + ) +} + +private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) { + hc = hashCons(new.(NewExpr).getInitializer()) or + hc = hashCons(new.(NewArrayExpr).getInitializer()) +} + +private predicate analyzableNewExpr(NewExpr new) { + strictcount(new.getAllocatedType()) = 1 and + ( + not exists(new.getAllocatorCall()) + or + strictcount(new.getAllocatorCall()) = 1 + ) and ( + not exists(new.getInitializer()) + or + strictcount(new.getInitializer()) = 1 + ) +} + +private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned, NewExpr new) { + analyzableNewExpr(new) and + t = new.getAllocatedType() and + ( + new.hasAlignedAllocation() and + aligned = true + or + not new.hasAlignedAllocation() and + aligned = false + ) + and + ( + exists(FunctionCall fc, HashCons head, HC_Alloc tail | + fc = new.getAllocatorCall() and + alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and + mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc) + ) + or + exists(FunctionCall fc | + fc = new.getAllocatorCall() and + ( + aligned = true and + fc.getNumberOfArguments() = 2 + or + aligned = false and + fc.getNumberOfArguments() = 1 + ) and + alloc = HC_EmptyAllocArgs(fc.getTarget()) + ) + or + not exists(new.getAllocatorCall()) and + alloc = HC_NoAlloc() + ) + and + ( + init = HC_HasInit(hashCons(new.getInitializer())) + or + not exists(new.getInitializer()) and + init = HC_NoInit() + ) +} + +private predicate analyzableNewArrayExpr(NewArrayExpr new) { + strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and + strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and + ( + not exists(new.getAllocatorCall()) + or + strictcount(new.getAllocatorCall().getFullyConverted()) = 1 + ) and ( + not exists(new.getInitializer()) + or + strictcount(new.getInitializer().getFullyConverted()) = 1 + ) +} + +private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned, + NewArrayExpr new) { + analyzableNewArrayExpr(new) and + t = new.getAllocatedType() and + ( + new.hasAlignedAllocation() and + aligned = true + or + not new.hasAlignedAllocation() and + aligned = false + ) + and + ( + exists(FunctionCall fc, HashCons head, HC_Alloc tail | + fc = new.getAllocatorCall() and + alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and + mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc) + ) + or + exists(FunctionCall fc | + fc = new.getAllocatorCall() and + ( + aligned = true and + fc.getNumberOfArguments() = 2 + or + aligned = false and + fc.getNumberOfArguments() = 1 + ) and + alloc = HC_EmptyAllocArgs(fc.getTarget()) + ) + or + not exists(new.getAllocatorCall()) and + alloc = HC_NoAlloc() + ) + and + ( + init = HC_HasInit(hashCons(new.getInitializer())) + or + not exists(new.getInitializer()) and + init = HC_NoInit() + ) +} + +private predicate analyzableSizeofType(SizeofTypeOperator e) { + strictcount(e.getType().getUnspecifiedType()) = 1 and + strictcount(e.getTypeOperand()) = 1 +} + +private predicate mk_SizeofType(Type t, SizeofTypeOperator e) { + analyzableSizeofType(e) and + t = e.getTypeOperand() +} + +private predicate analyzableSizeofExpr(Expr e) { + e instanceof SizeofExprOperator and + strictcount(e.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_SizeofExpr(HashCons child, SizeofExprOperator e) { + analyzableSizeofExpr(e) and + child = hashCons(e.getAChild()) +} + +private predicate analyzableAlignofType(AlignofTypeOperator e) { + strictcount(e.getType().getUnspecifiedType()) = 1 and + strictcount(e.getTypeOperand()) = 1 +} + +private predicate mk_AlignofType(Type t, AlignofTypeOperator e) { + analyzableAlignofType(e) and + t = e.getTypeOperand() +} + +private predicate analyzableAlignofExpr(AlignofExprOperator e) { + strictcount(e.getExprOperand()) = 1 +} + +private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) { + analyzableAlignofExpr(e) and + child = hashCons(e.getAChild()) +} + /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -514,6 +744,36 @@ cached HashCons hashCons(Expr e) { result = HC_MemberFunctionCall(fcn, qual, args) ) or + exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned + | mk_NewExpr(t, alloc, init, aligned, e) and + result = HC_NewExpr(t, alloc, init) + ) + or + exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned + | mk_NewArrayExpr(t, alloc, init, aligned, e) and + result = HC_NewArrayExpr(t, alloc, init) + ) + or + exists(Type t + | mk_SizeofType(t, e) and + result = HC_SizeofType(t) + ) + or + exists(HashCons child + | mk_SizeofExpr(child, e) and + result = HC_SizeofExpr(child) + ) + or + exists(Type t + | mk_AlignofType(t, e) and + result = HC_AlignofType(t) + ) + or + exists(HashCons child + | mk_AlignofExpr(child, e) and + result = HC_AlignofExpr(child) + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -545,5 +805,11 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableArrayAccess(e) and kind = "ArrayAccess") or (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or (analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or - (analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") + (analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") or + (analyzableNewExpr(e) and kind = "NewExpr") or + (analyzableNewArrayExpr(e) and kind = "NewArrayExpr") or + (analyzableSizeofType(e) and kind = "SizeofTypeOperator") or + (analyzableSizeofExpr(e) and kind = "SizeofExprOperator") or + (analyzableAlignofType(e) and kind = "AlignofTypeOperator") or + (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 7f4a5d6ed383..c1d9617206f9 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,9 +38,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 270:c19-c19 271:c19-c19 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 270:c15-c15 270:c22-c22 271:c15-c15 271:c22-c22 272:c15-c15 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -73,3 +73,27 @@ | test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 | | test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 | | test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | +| test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 | +| test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 253:c25-c35 254:c25-c35 | +| test.cpp:206:25:206:43 | alignof(int_holder) | 206:c25-c43 206:c3-c21 | +| test.cpp:208:27:208:27 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 | +| test.cpp:209:10:209:15 | holder | 209:c10-c15 209:c29-c34 | +| test.cpp:209:10:209:18 | (...) | 209:c10-c18 209:c29-c37 | +| test.cpp:209:17:209:17 | x | 209:c17-c17 209:c36-c36 | +| test.cpp:209:22:209:37 | sizeof() | 209:c22-c37 209:c3-c18 | +| test.cpp:210:10:210:11 | (...) | 210:c10-c11 211:c11-c12 211:c24-c25 | +| test.cpp:211:16:211:25 | alignof() | 211:c16-c25 211:c3-c12 | +| test.cpp:247:3:247:12 | new | 247:c3-c12 248:c3-c12 | +| test.cpp:253:16:253:36 | new[] | 253:c16-c36 254:c16-c36 | +| test.cpp:256:3:256:21 | new | 256:c3-c21 257:c3-c21 | +| test.cpp:256:7:256:10 | (void *)... | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 | +| test.cpp:256:7:256:10 | ptr1 | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 | +| test.cpp:258:7:258:10 | (void *)... | 258:c7-c10 262:c11-c14 | +| test.cpp:258:7:258:10 | ptr2 | 258:c7-c10 262:c11-c14 | +| test.cpp:260:3:260:25 | new | 260:c3-c25 261:c3-c25 | +| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 | +| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 | +| test.cpp:264:3:264:19 | new | 264:c3-c19 265:c3-c19 | +| test.cpp:267:3:267:23 | new[] | 267:c3-c23 268:c3-c23 | +| test.cpp:267:21:267:22 | 10 | 267:c21-c22 268:c21-c22 92:c15-c16 | +| test.cpp:272:19:272:19 | 3 | 272:c19-c19 35:c16-c16 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index ed31f21d35aa..9522ab17b4bb 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -184,3 +184,90 @@ int test13(int y) { int test14(int y) { return SQUARE(y); } + +typedef struct { + int x; + char y; +} padded_t; + +typedef struct { + int x; +} int_holder; + +typedef unsigned long size_t; + +void *malloc(size_t size); + +int test15(int x) { + sizeof(padded_t); + alignof(padded_t); + sizeof(padded_t); + sizeof(int_holder) + sizeof(int); + alignof(int_holder) + alignof(int_holder); + + int_holder holder = {x: x}; + sizeof(holder.x) + sizeof(holder.x); + sizeof(x); + alignof(x) + alignof(x); +} + +static void *operator new(size_t size) { + return malloc(size); +} + +static void *operator new(size_t size, void *placement) { + return placement; +} + +static void *operator new(size_t size, size_t alignment, void *placement) { + return placement; +} + +static void *operator new(size_t size, size_t alignment) { + return malloc(size); +} + +static void *operator new[](size_t size) { + return malloc(size); +} + +static void *operator new[](size_t size, void *placement) { + return placement; +} + +static void *operator new[](size_t size, size_t alignment, void *placement) { + return placement; +} + +static void *operator new[](size_t size, size_t alignment) { + return malloc(size); +} + +void test16() { + new int(1); + new int(1); + new int(2); + + int x; + + char *ptr1 = new char[sizeof(int)]; + char *ptr2 = new char[sizeof(int)]; + + new(ptr1) IntHolder; + new(ptr1) IntHolder; + new(ptr2) IntHolder; + + new(32, ptr1) IntHolder; + new(32, ptr1) IntHolder; + new(32, ptr2) IntHolder; + + new(32) IntHolder; + new(32) IntHolder; + + new(32) IntHolder[10]; + new(32) IntHolder[10]; + + new(32) int[2] {1, 2}; + new(32) int[2] {1, 2}; + new(32) int[2] {3, 4}; +} From 8f446aa9cce5ee4a3331dec8e22ce6e6675804c6 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 28 Aug 2018 13:41:46 -0700 Subject: [PATCH 022/110] C++: fix handling of aligned allocators --- .../code/cpp/valuenumbering/HashCons.qll | 44 ++++++++++++------- .../valuenumbering/HashCons/HashCons.expected | 20 ++++----- .../valuenumbering/HashCons/test.cpp | 1 + 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index fedaabf47e55..1f636bc5ab6a 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -92,11 +92,11 @@ private cached newtype HCBase = mk_MemberFunctionCall(trg, qual, args, _) } or - HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init) { - mk_NewExpr(t, alloc, init, _, _) + HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align) { + mk_NewExpr(t, alloc, init, align, _, _) } or - HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init) { - mk_NewArrayExpr(t, alloc, init, _, _) + HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align) { + mk_NewArrayExpr(t, alloc, init, align, _, _) } or HC_SizeofType(Type t) {mk_SizeofType(t, _)} @@ -123,11 +123,18 @@ private newtype HC_Alloc = } or HC_NoAlloc() + +/** Used to implement optional init on `new` expressions */ private newtype HC_Init = HC_NoInit() or HC_HasInit(HashCons hc) {mk_HasInit(hc, _)} +private newtype HC_Align = + HC_NoAlign() + or + HC_HasAlign(HashCons hc) {mk_HasAlign(hc, _)} + /** Used to implement hash-consing of argument lists */ private newtype HC_Args = HC_EmptyArgs(Function fcn) { @@ -525,6 +532,10 @@ private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) { hc = hashCons(new.(NewArrayExpr).getInitializer()) } +private predicate mk_HasAlign(HashCons hc, NewOrNewArrayExpr new) { + hc = hashCons(new.getAlignmentArgument()) +} + private predicate analyzableNewExpr(NewExpr new) { strictcount(new.getAllocatedType()) = 1 and ( @@ -538,14 +549,16 @@ private predicate analyzableNewExpr(NewExpr new) { ) } -private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned, NewExpr new) { +private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned, + NewExpr new) { analyzableNewExpr(new) and t = new.getAllocatedType() and ( - new.hasAlignedAllocation() and + align = HC_HasAlign(hashCons(new.getAlignmentArgument())) and aligned = true or not new.hasAlignedAllocation() and + align = HC_NoAlign() and aligned = false ) and @@ -594,15 +607,16 @@ private predicate analyzableNewArrayExpr(NewArrayExpr new) { ) } -private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned, - NewArrayExpr new) { +private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, + boolean aligned, NewArrayExpr new) { analyzableNewArrayExpr(new) and t = new.getAllocatedType() and ( - new.hasAlignedAllocation() and + align = HC_HasAlign(hashCons(new.getAlignmentArgument())) and aligned = true or not new.hasAlignedAllocation() and + align = HC_NoAlign() and aligned = false ) and @@ -744,14 +758,14 @@ cached HashCons hashCons(Expr e) { result = HC_MemberFunctionCall(fcn, qual, args) ) or - exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned - | mk_NewExpr(t, alloc, init, aligned, e) and - result = HC_NewExpr(t, alloc, init) + exists(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned + | mk_NewExpr(t, alloc, init, align, aligned, e) and + result = HC_NewExpr(t, alloc, init, align) ) or - exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned - | mk_NewArrayExpr(t, alloc, init, aligned, e) and - result = HC_NewArrayExpr(t, alloc, init) + exists(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned + | mk_NewArrayExpr(t, alloc, init, align, aligned, e) and + result = HC_NewArrayExpr(t, alloc, init, align) ) or exists(Type t diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index c1d9617206f9..e2d2c341f07e 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,9 +38,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 270:c19-c19 271:c19-c19 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c19-c19 272:c19-c19 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 270:c15-c15 270:c22-c22 271:c15-c15 271:c22-c22 272:c15-c15 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c15-c15 271:c22-c22 272:c15-c15 272:c22-c22 273:c15-c15 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -86,14 +86,14 @@ | test.cpp:247:3:247:12 | new | 247:c3-c12 248:c3-c12 | | test.cpp:253:16:253:36 | new[] | 253:c16-c36 254:c16-c36 | | test.cpp:256:3:256:21 | new | 256:c3-c21 257:c3-c21 | -| test.cpp:256:7:256:10 | (void *)... | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 | -| test.cpp:256:7:256:10 | ptr1 | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 | +| test.cpp:256:7:256:10 | (void *)... | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 263:c11-c14 | +| test.cpp:256:7:256:10 | ptr1 | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 263:c11-c14 | | test.cpp:258:7:258:10 | (void *)... | 258:c7-c10 262:c11-c14 | | test.cpp:258:7:258:10 | ptr2 | 258:c7-c10 262:c11-c14 | | test.cpp:260:3:260:25 | new | 260:c3-c25 261:c3-c25 | -| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 | -| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 | -| test.cpp:264:3:264:19 | new | 264:c3-c19 265:c3-c19 | -| test.cpp:267:3:267:23 | new[] | 267:c3-c23 268:c3-c23 | -| test.cpp:267:21:267:22 | 10 | 267:c21-c22 268:c21-c22 92:c15-c16 | -| test.cpp:272:19:272:19 | 3 | 272:c19-c19 35:c16-c16 | +| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 | +| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 | +| test.cpp:265:3:265:19 | new | 265:c3-c19 266:c3-c19 | +| test.cpp:268:3:268:23 | new[] | 268:c3-c23 269:c3-c23 | +| test.cpp:268:21:268:22 | 10 | 268:c21-c22 269:c21-c22 92:c15-c16 | +| test.cpp:273:19:273:19 | 3 | 273:c19-c19 35:c16-c16 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 9522ab17b4bb..e3980bdd11cb 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -260,6 +260,7 @@ void test16() { new(32, ptr1) IntHolder; new(32, ptr1) IntHolder; new(32, ptr2) IntHolder; + new(16, ptr1) IntHolder; new(32) IntHolder; new(32) IntHolder; From 752f39b5374e5fe93e95c17432bfc4b796e578c4 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 10:51:27 -0700 Subject: [PATCH 023/110] C++: initial support for aggregate initializers --- .../code/cpp/valuenumbering/HashCons.qll | 130 +++++++++++++++++- .../valuenumbering/HashCons/HashCons.expected | 13 +- .../valuenumbering/HashCons/test.cpp | 25 ++++ 3 files changed, 163 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 1f636bc5ab6a..9cd9cc424cec 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -107,6 +107,14 @@ private cached newtype HCBase = or HC_AlignofExpr(HashCons child) {mk_AlignofExpr(child, _)} or + HC_ClassAggregateLiteral(Class c, HC_Fields hcf) { + mk_ClassAggregateLiteral(c, hcf, _) + } + or + HC_ArrayAggregateLiteral(Type t, HC_Array hca) { + mk_ArrayAggregateLiteral(t, hca, _) + } + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } @@ -144,6 +152,30 @@ private newtype HC_Args = mk_ArgCons(fcn, hc, i, list, _) } +/** + * Used to implement hash-consing of struct initizializers. + */ +private newtype HC_Fields = + HC_EmptyFields(Class c) { + exists(ClassAggregateLiteral cal | + c = cal.getType().getUnspecifiedType() + ) + } + or + HC_FieldCons(Class c, int i, Field f, HashCons hc, HC_Fields hcf) { + mk_FieldCons(c, i, f, hc, hcf, _) + } + +private newtype HC_Array = + HC_EmptyArray(Type t) { + exists(ArrayAggregateLiteral aal | + aal.getType() = t + ) + } + or + HC_ArrayCons(Type t, int i, HashCons hc, HC_Array hca) { + mk_ArrayCons(t, i, hc, hca, _) + } /** * HashCons is the hash-cons of an expression. The relationship between `Expr` * and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple @@ -188,6 +220,8 @@ class HashCons extends HCBase { if this instanceof HC_SizeofExpr then result = "SizeofExprOperator" else if this instanceof HC_AlignofType then result = "AlignofTypeOperator" else if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else + if this instanceof HC_ArrayAggregateLiteral then result = "ArrayAggregateLiteral" else + if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else result = "error" } @@ -690,6 +724,87 @@ private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) { child = hashCons(e.getAChild()) } +private predicate mk_FieldCons(Class c, int i, Field f, HashCons hc, HC_Fields hcf, + ClassAggregateLiteral cal) { + analyzableClassAggregateLiteral(cal) and + cal.getType().getUnspecifiedType() = c and + exists(Expr e | + e = cal.getFieldExpr(f).getFullyConverted() and + e = cal.getChild(i).getFullyConverted() and + hc = hashCons(e) and + ( + exists(HashCons head, Field f2, HC_Fields tail | + hcf = HC_FieldCons(c, i-1, f2, head, tail) and + cal.getChild(i-1).getFullyConverted() = cal.getFieldExpr(f2).getFullyConverted() and + mk_FieldCons(c, i-1, f2, head, tail, cal) + + ) + or + i = 0 and + hcf = HC_EmptyFields(c) + ) + ) +} + +private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) { + forall(int i | + exists(cal.getChild(i)) | + strictcount(cal.getChild(i).getFullyConverted()) = 1 and + strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 + ) +} + +private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregateLiteral cal) { + analyzableClassAggregateLiteral(cal) and + c = cal.getType().getUnspecifiedType() and + ( + exists(HC_Fields tail, Expr e, Field f | + e = cal.getChild(cal.getNumChild() - 1).getFullyConverted() and + e = cal.getFieldExpr(f).getFullyConverted() and + hcf = HC_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail) and + mk_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail, cal) + ) + or + cal.getNumChild() = 0 and + hcf = HC_EmptyFields(c) + ) +} + +private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) { + forall(int i | + exists(aal.getChild(i)) | + strictcount(aal.getChild(i).getFullyConverted()) = 1 + ) +} + +private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) { + t = aal.getType().getUnspecifiedType() and + hc = hashCons(aal.getChild(i)) and + ( + exists(HC_Array tail, HashCons head | + hca = HC_ArrayCons(t, i - 1, head, tail) and + mk_ArrayCons(t, i-1, head, tail, aal) + ) + or + i = 0 and + hca = HC_EmptyArray(t) + ) +} + +private predicate mk_ArrayAggregateLiteral(Type t, HC_Array hca, ArrayAggregateLiteral aal) { + t = aal.getType().getUnspecifiedType() and + ( + exists(HashCons head, HC_Array tail | + hca = HC_ArrayCons(t, aal.getNumChild() - 1, head, tail) and + mk_ArrayCons(t, aal.getNumChild() - 1, head, tail, aal) + ) + or + aal.getNumChild() = 0 and + hca = HC_EmptyArray(t) + ) +} + + /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -788,6 +903,16 @@ cached HashCons hashCons(Expr e) { result = HC_AlignofExpr(child) ) or + exists(Class c, HC_Fields hfc + | mk_ClassAggregateLiteral(c, hfc, e) and + result = HC_ClassAggregateLiteral(c, hfc) + ) + or + exists(Type t, HC_Array hca + | mk_ArrayAggregateLiteral(t, hca, e) and + result = HC_ArrayAggregateLiteral(t, hca) + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -825,5 +950,8 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableSizeofType(e) and kind = "SizeofTypeOperator") or (analyzableSizeofExpr(e) and kind = "SizeofExprOperator") or (analyzableAlignofType(e) and kind = "AlignofTypeOperator") or - (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") + (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") or + (analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral") or + (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") + } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index e2d2c341f07e..a1def819e2cf 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,9 +38,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c19-c19 272:c19-c19 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c19-c19 272:c19-c19 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c15-c15 271:c22-c22 272:c15-c15 272:c22-c22 273:c15-c15 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c15-c15 271:c22-c22 272:c15-c15 272:c22-c22 273:c15-c15 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -91,9 +91,14 @@ | test.cpp:258:7:258:10 | (void *)... | 258:c7-c10 262:c11-c14 | | test.cpp:258:7:258:10 | ptr2 | 258:c7-c10 262:c11-c14 | | test.cpp:260:3:260:25 | new | 260:c3-c25 261:c3-c25 | -| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 | -| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 | +| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | +| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | | test.cpp:265:3:265:19 | new | 265:c3-c19 266:c3-c19 | | test.cpp:268:3:268:23 | new[] | 268:c3-c23 269:c3-c23 | | test.cpp:268:21:268:22 | 10 | 268:c21-c22 269:c21-c22 92:c15-c16 | +| test.cpp:271:3:271:23 | new[] | 271:c3-c23 272:c3-c23 | +| test.cpp:271:3:271:23 | {...} | 271:c3-c23 272:c3-c23 | | test.cpp:273:19:273:19 | 3 | 273:c19-c19 35:c16-c16 | +| test.cpp:277:3:277:19 | new[] | 277:c3-c19 278:c3-c19 | +| test.cpp:277:3:277:19 | {...} | 277:c3-c19 278:c3-c19 | +| test.cpp:287:15:290:3 | {...} | 287:c15-c3 291:c15-c3 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index e3980bdd11cb..99319ed7eb81 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -271,4 +271,29 @@ void test16() { new(32) int[2] {1, 2}; new(32) int[2] {1, 2}; new(32) int[2] {3, 4}; + new(32) int[2] {1, 1}; + new(32) int[2] {2, 2}; + + new(32) int[2] {}; + new(32) int[2] {}; +} + +typedef struct point{ + int x; + int y; +} point_t; + +void test17() { + point_t p1 = { + 1, + 2 + }; + point_t p2 = { + 1, + 2 + }; + point_t p3 = { + 2, + 1 + }; } From 85cfb0202fed0503bf4c62e8e519a06f59d05e0d Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 11:06:28 -0700 Subject: [PATCH 024/110] C++: add HashCons for delete expressions --- .../code/cpp/valuenumbering/HashCons.qll | 40 +++++++++++++++++-- .../valuenumbering/HashCons/HashCons.expected | 28 +++++++------ .../valuenumbering/HashCons/test.cpp | 16 ++++---- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 9cd9cc424cec..35311d4f9c68 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -115,6 +115,10 @@ private cached newtype HCBase = mk_ArrayAggregateLiteral(t, hca, _) } or + HC_DeleteExpr(HashCons child) {mk_DeleteExpr(child, _)} + or + HC_DeleteArrayExpr(HashCons child) {mk_DeleteArrayExpr(child, _)} + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } @@ -222,6 +226,8 @@ class HashCons extends HCBase { if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else if this instanceof HC_ArrayAggregateLiteral then result = "ArrayAggregateLiteral" else if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else + if this instanceof HC_DeleteExpr then result = "DeleteExpr" else + if this instanceof HC_DeleteArrayExpr then result = "DeleteArrayExpr" else result = "error" } @@ -685,6 +691,24 @@ private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align ) } +private predicate analyzableDeleteExpr(DeleteExpr e) { + strictcount(e.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_DeleteExpr(HashCons hc, DeleteExpr e) { + analyzableDeleteExpr(e) and + hc = hashCons(e.getAChild().getFullyConverted()) +} + +private predicate analyzableDeleteArrayExpr(DeleteArrayExpr e) { + strictcount(e.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_DeleteArrayExpr(HashCons hc, DeleteArrayExpr e) { + analyzableDeleteArrayExpr(e) and + hc = hashCons(e.getAChild().getFullyConverted()) +} + private predicate analyzableSizeofType(SizeofTypeOperator e) { strictcount(e.getType().getUnspecifiedType()) = 1 and strictcount(e.getTypeOperand()) = 1 @@ -804,7 +828,6 @@ private predicate mk_ArrayAggregateLiteral(Type t, HC_Array hca, ArrayAggregateL ) } - /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -913,6 +936,16 @@ cached HashCons hashCons(Expr e) { result = HC_ArrayAggregateLiteral(t, hca) ) or + exists(HashCons child + | mk_DeleteExpr(child, e) and + result = HC_DeleteExpr(child) + ) + or + exists(HashCons child + | mk_DeleteArrayExpr(child, e) and + result = HC_DeleteArrayExpr(child) + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -952,6 +985,7 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableAlignofType(e) and kind = "AlignofTypeOperator") or (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") or (analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral") or - (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") - + (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") or + (analyzableDeleteExpr(e) and kind = "DeleteExpr") or + (analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index a1def819e2cf..6267a6bf60f9 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,9 +38,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c19-c19 272:c19-c19 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c15-c15 271:c22-c22 272:c15-c15 272:c22-c22 273:c15-c15 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c24-c24 271:c31-c31 272:c24-c24 272:c31-c31 273:c24-c24 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -85,20 +85,22 @@ | test.cpp:211:16:211:25 | alignof() | 211:c16-c25 211:c3-c12 | | test.cpp:247:3:247:12 | new | 247:c3-c12 248:c3-c12 | | test.cpp:253:16:253:36 | new[] | 253:c16-c36 254:c16-c36 | -| test.cpp:256:3:256:21 | new | 256:c3-c21 257:c3-c21 | -| test.cpp:256:7:256:10 | (void *)... | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 263:c11-c14 | -| test.cpp:256:7:256:10 | ptr1 | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 263:c11-c14 | -| test.cpp:258:7:258:10 | (void *)... | 258:c7-c10 262:c11-c14 | -| test.cpp:258:7:258:10 | ptr2 | 258:c7-c10 262:c11-c14 | -| test.cpp:260:3:260:25 | new | 260:c3-c25 261:c3-c25 | -| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | -| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c7-c8 272:c7-c8 273:c7-c8 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | +| test.cpp:256:3:256:28 | delete | 256:c3-c28 257:c3-c28 | +| test.cpp:256:10:256:28 | new | 256:c10-c28 257:c10-c28 | +| test.cpp:256:14:256:17 | (void *)... | 256:c14-c17 257:c14-c17 260:c18-c21 261:c20-c23 263:c11-c14 | +| test.cpp:256:14:256:17 | ptr1 | 256:c14-c17 257:c14-c17 260:c18-c21 261:c20-c23 263:c11-c14 | +| test.cpp:258:14:258:17 | (void *)... | 258:c14-c17 262:c11-c14 | +| test.cpp:258:14:258:17 | ptr2 | 258:c14-c17 262:c11-c14 | +| test.cpp:260:10:260:32 | new | 260:c10-c32 261:c12-c34 | +| test.cpp:260:14:260:15 | 32 | 260:c14-c15 261:c16-c17 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c16-c17 272:c16-c17 273:c16-c17 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | +| test.cpp:260:14:260:15 | (size_t)... | 260:c14-c15 261:c16-c17 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c16-c17 272:c16-c17 273:c16-c17 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | | test.cpp:265:3:265:19 | new | 265:c3-c19 266:c3-c19 | | test.cpp:268:3:268:23 | new[] | 268:c3-c23 269:c3-c23 | | test.cpp:268:21:268:22 | 10 | 268:c21-c22 269:c21-c22 92:c15-c16 | -| test.cpp:271:3:271:23 | new[] | 271:c3-c23 272:c3-c23 | -| test.cpp:271:3:271:23 | {...} | 271:c3-c23 272:c3-c23 | -| test.cpp:273:19:273:19 | 3 | 273:c19-c19 35:c16-c16 | +| test.cpp:271:3:271:32 | delete[] | 271:c3-c32 272:c3-c32 | +| test.cpp:271:12:271:32 | new[] | 271:c12-c32 272:c12-c32 | +| test.cpp:271:12:271:32 | {...} | 271:c12-c32 272:c12-c32 | +| test.cpp:273:28:273:28 | 3 | 273:c28-c28 35:c16-c16 | | test.cpp:277:3:277:19 | new[] | 277:c3-c19 278:c3-c19 | | test.cpp:277:3:277:19 | {...} | 277:c3-c19 278:c3-c19 | | test.cpp:287:15:290:3 | {...} | 287:c15-c3 291:c15-c3 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 99319ed7eb81..232eb380549b 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -253,12 +253,12 @@ void test16() { char *ptr1 = new char[sizeof(int)]; char *ptr2 = new char[sizeof(int)]; - new(ptr1) IntHolder; - new(ptr1) IntHolder; - new(ptr2) IntHolder; + delete new(ptr1) IntHolder; + delete new(ptr1) IntHolder; + delete new(ptr2) IntHolder; - new(32, ptr1) IntHolder; - new(32, ptr1) IntHolder; + delete new(32, ptr1) IntHolder; + delete[] new(32, ptr1) IntHolder; new(32, ptr2) IntHolder; new(16, ptr1) IntHolder; @@ -268,9 +268,9 @@ void test16() { new(32) IntHolder[10]; new(32) IntHolder[10]; - new(32) int[2] {1, 2}; - new(32) int[2] {1, 2}; - new(32) int[2] {3, 4}; + delete[] new(32) int[2] {1, 2}; + delete[] new(32) int[2] {1, 2}; + delete[] new(32) int[2] {3, 4}; new(32) int[2] {1, 1}; new(32) int[2] {2, 2}; From 8189798f43aeb63ba52ad60e925bb02ebec4f4c4 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 14:01:05 -0700 Subject: [PATCH 025/110] C++: HashCons for throw --- .../code/cpp/valuenumbering/HashCons.qll | 37 ++++++++++++++++++- .../valuenumbering/HashCons/HashCons.expected | 7 +++- .../valuenumbering/HashCons/test.cpp | 9 +++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 35311d4f9c68..850033a76b7c 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -119,6 +119,10 @@ private cached newtype HCBase = or HC_DeleteArrayExpr(HashCons child) {mk_DeleteArrayExpr(child, _)} or + HC_ThrowExpr(HashCons child) {mk_ThrowExpr(child, _)} + or + HC_ReThrowExpr() + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } @@ -228,6 +232,8 @@ class HashCons extends HCBase { if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else if this instanceof HC_DeleteExpr then result = "DeleteExpr" else if this instanceof HC_DeleteArrayExpr then result = "DeleteArrayExpr" else + if this instanceof HC_ThrowExpr then result = "ThrowExpr" else + if this instanceof HC_ReThrowExpr then result = "ReThrowExpr" else result = "error" } @@ -828,6 +834,23 @@ private predicate mk_ArrayAggregateLiteral(Type t, HC_Array hca, ArrayAggregateL ) } +private predicate analyzableThrowExpr(ThrowExpr te) { + strictcount(te.getExpr().getFullyConverted()) = 1 +} + +private predicate mk_ThrowExpr(HashCons hc, ThrowExpr te) { + analyzableThrowExpr(te) and + hc.getAnExpr() = te.getExpr().getFullyConverted() +} + +private predicate analyzableReThrowExpr(ReThrowExpr rte) { + any() +} + +private predicate mk_ReThrowExpr(ReThrowExpr te) { + any() +} + /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -946,6 +969,16 @@ cached HashCons hashCons(Expr e) { result = HC_DeleteArrayExpr(child) ) or + exists(HashCons child + | mk_ThrowExpr(child, e) and + result = HC_ThrowExpr(child) + ) + or + ( + mk_ReThrowExpr(e) and + result = HC_ReThrowExpr() + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -987,5 +1020,7 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral") or (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") or (analyzableDeleteExpr(e) and kind = "DeleteExpr") or - (analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr") + (analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr") or + (analyzableThrowExpr(e) and kind = "ThrowExpr") or + (analyzableReThrowExpr(e) and kind = "ReThrowExpr") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 6267a6bf60f9..84a50da1fd17 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -38,9 +38,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 302:c9-c9 303:c9-c9 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c24-c24 271:c31-c31 272:c24-c24 272:c31-c31 273:c24-c24 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c24-c24 271:c31-c31 272:c24-c24 272:c31-c31 273:c24-c24 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 304:c9-c9 305:c9-c9 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -104,3 +104,6 @@ | test.cpp:277:3:277:19 | new[] | 277:c3-c19 278:c3-c19 | | test.cpp:277:3:277:19 | {...} | 277:c3-c19 278:c3-c19 | | test.cpp:287:15:290:3 | {...} | 287:c15-c3 291:c15-c3 | +| test.cpp:302:3:302:9 | throw ... | 302:c3-c9 303:c3-c9 | +| test.cpp:304:3:304:9 | throw ... | 304:c3-c9 305:c3-c9 | +| test.cpp:306:3:306:7 | re-throw exception | 306:c3-c7 307:c3-c7 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 232eb380549b..36c8455a40b9 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -297,3 +297,12 @@ void test17() { 1 }; } + +void test18() { + throw 1; + throw 1; + throw 2; + throw 2; + throw; + throw; +} From cfeed30a89ec44e01593b4ce96f08e8555e179cc Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 14:25:09 -0700 Subject: [PATCH 026/110] C++: Hashcons tests for ArrayExpr --- .../valuenumbering/HashCons/HashCons.expected | 8 ++++++-- .../test/library-tests/valuenumbering/HashCons/test.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 84a50da1fd17..0680e7d8f2c7 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -21,7 +21,6 @@ | test.cpp:43:7:43:24 | ... + ... | 43:c7-c24 45:c7-c24 | | test.cpp:43:12:43:13 | p1 | 43:c12-c13 45:c12-c13 | | test.cpp:43:17:43:24 | global03 | 43:c17-c24 45:c17-c24 | -| test.cpp:44:9:44:9 | 0 | 44:c9-c9 51:c25-c25 88:c12-c12 | | test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 | | test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 | | test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 | @@ -38,7 +37,7 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 302:c9-c9 303:c9-c9 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 302:c9-c9 303:c9-c9 313:c5-c5 314:c5-c5 316:c5-c5 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | | test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c24-c24 271:c31-c31 272:c24-c24 272:c31-c31 273:c24-c24 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 304:c9-c9 305:c9-c9 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | @@ -107,3 +106,8 @@ | test.cpp:302:3:302:9 | throw ... | 302:c3-c9 303:c3-c9 | | test.cpp:304:3:304:9 | throw ... | 304:c3-c9 305:c3-c9 | | test.cpp:306:3:306:7 | re-throw exception | 306:c3-c7 307:c3-c7 | +| test.cpp:311:3:311:3 | x | 311:c3-c3 312:c3-c3 313:c3-c3 314:c3-c3 | +| test.cpp:311:3:311:6 | access to array | 311:c3-c6 312:c3-c6 | +| test.cpp:311:5:311:5 | 0 | 311:c5-c5 312:c5-c5 315:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 | +| test.cpp:313:3:313:6 | access to array | 313:c3-c6 314:c3-c6 | +| test.cpp:315:3:315:3 | y | 315:c3-c3 316:c3-c3 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 36c8455a40b9..5dcbf8489210 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -306,3 +306,12 @@ void test18() { throw; throw; } + +void test19(int *x, int *y) { + x[0]; + x[0]; + x[1]; + x[1]; + y[0]; + y[1]; +} From 06a3e8fc76e87d30dcca01767dae1d08f331575c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 15:15:51 -0700 Subject: [PATCH 027/110] C++: Hashcons for ?:, ExprCall, and weird stuff --- .../code/cpp/valuenumbering/HashCons.qll | 178 +++++++++++++++--- .../valuenumbering/HashCons/HashCons.expected | 10 + .../valuenumbering/HashCons/test.cpp | 20 ++ 3 files changed, 180 insertions(+), 28 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 850033a76b7c..0765ff92cf92 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -87,6 +87,9 @@ private cached newtype HCBase = HC_NonmemberFunctionCall(Function fcn, HC_Args args) { mk_NonmemberFunctionCall(fcn, args, _) } + or HC_ExprCall(HashCons hc, HC_Args args) { + mk_ExprCall(hc, args, _) + } or HC_MemberFunctionCall(Function trg, HashCons qual, HC_Args args) { mk_MemberFunctionCall(trg, qual, args, _) @@ -107,6 +110,12 @@ private cached newtype HCBase = or HC_AlignofExpr(HashCons child) {mk_AlignofExpr(child, _)} or + HC_UuidofOperator(Type t) {mk_UuidofOperator(t, _)} + or + HC_TypeidType(Type t) {mk_TypeidType(t, _)} + or + HC_TypeidExpr(HashCons child) {mk_TypeidExpr(child, _)} + or HC_ClassAggregateLiteral(Class c, HC_Fields hcf) { mk_ClassAggregateLiteral(c, hcf, _) } @@ -123,6 +132,14 @@ private cached newtype HCBase = or HC_ReThrowExpr() or + HC_ConditionalExpr(HashCons cond, HashCons trueHC, HashCons falseHC) { + mk_ConditionalExpr(cond, trueHC, falseHC, _) + } + or + HC_NoExceptExpr(HashCons child) { + mk_NoExceptExpr(child, _) + } + or // Any expression that is not handled by the cases above is // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } @@ -153,11 +170,11 @@ private newtype HC_Align = /** Used to implement hash-consing of argument lists */ private newtype HC_Args = - HC_EmptyArgs(Function fcn) { + HC_EmptyArgs() { any() } - or HC_ArgCons(Function fcn, HashCons hc, int i, HC_Args list) { - mk_ArgCons(fcn, hc, i, list, _) + or HC_ArgCons(HashCons hc, int i, HC_Args list) { + mk_ArgCons(hc, i, list, _) } /** @@ -228,12 +245,18 @@ class HashCons extends HCBase { if this instanceof HC_SizeofExpr then result = "SizeofExprOperator" else if this instanceof HC_AlignofType then result = "AlignofTypeOperator" else if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else + if this instanceof HC_UuidofOperator then result = "UuidofOperator" else + if this instanceof HC_TypeidType then result = "TypeidType" else + if this instanceof HC_TypeidExpr then result = "TypeidExpr" else if this instanceof HC_ArrayAggregateLiteral then result = "ArrayAggregateLiteral" else if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else if this instanceof HC_DeleteExpr then result = "DeleteExpr" else if this instanceof HC_DeleteArrayExpr then result = "DeleteArrayExpr" else if this instanceof HC_ThrowExpr then result = "ThrowExpr" else if this instanceof HC_ReThrowExpr then result = "ReThrowExpr" else + if this instanceof HC_ExprCall then result = "ExprCall" else + if this instanceof HC_ConditionalExpr then result = "ConditionalExpr" else + if this instanceof HC_NoExceptExpr then result = "NoExceptExpr" else result = "error" } @@ -473,19 +496,40 @@ private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionC analyzableNonmemberFunctionCall(fc) and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) ) or fc.getNumberOfArguments() = 0 and - args = HC_EmptyArgs(fcn) + args = HC_EmptyArgs() + ) +} + +private predicate analyzableExprCall(ExprCall ec) { + forall(int i | + exists(ec.getArgument(i)) | + strictcount(ec.getArgument(i).getFullyConverted()) = 1 + ) and + strictcount(ec.getExpr().getFullyConverted()) = 1 +} + +private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) { + hc.getAnExpr() = ec.getExpr() and + ( + exists(HashCons head, HC_Args tail | + args = HC_ArgCons(head, ec.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, ec.getNumberOfArguments() - 1, tail, ec) + ) + or + ec.getNumberOfArguments() = 0 and + args = HC_EmptyArgs() ) } private predicate analyzableMemberFunctionCall( FunctionCall fc) { forall(int i | - exists(fc.getArgument(i)) | + exists(fc.getArgument(i)) | strictcount(fc.getArgument(i).getFullyConverted()) = 1 ) and strictcount(fc.getTarget()) = 1 and @@ -503,40 +547,39 @@ private predicate mk_MemberFunctionCall( hashCons(fc.getQualifier().getFullyConverted()) = qual and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc) + args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and + mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) ) or fc.getNumberOfArguments() = 0 and - args = HC_EmptyArgs(fcn) + args = HC_EmptyArgs() ) } -private predicate analyzableFunctionCall( - FunctionCall fc -) { - analyzableNonmemberFunctionCall(fc) +private predicate analyzableCall(Call c) { + analyzableNonmemberFunctionCall(c) + or + analyzableMemberFunctionCall(c) or - analyzableMemberFunctionCall(fc) + analyzableExprCall(c) } /** * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, FunctionCall fc) { - analyzableFunctionCall(fc) and - fc.getTarget() = fcn and - hc = hashCons(fc.getArgument(i).getFullyConverted()) and +private predicate mk_ArgCons(HashCons hc, int i, HC_Args list,Call c) { + analyzableCall(c) and + hc = hashCons(c.getArgument(i).getFullyConverted()) and ( exists(HashCons head, HC_Args tail | - list = HC_ArgCons(fcn, head, i - 1, tail) and - mk_ArgCons(fcn, head, i - 1, tail, fc) and + list = HC_ArgCons(head, i - 1, tail) and + mk_ArgCons(head, i - 1, tail, c) and i > 0 ) or i = 0 and - list = HC_EmptyArgs(fcn) + list = HC_EmptyArgs() ) } @@ -545,14 +588,15 @@ private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, Fun * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, FunctionCall fc) { - analyzableFunctionCall(fc) and - fc.getTarget() = fcn and - hc = hashCons(fc.getArgument(i).getFullyConverted()) and +private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, + Call c) { + analyzableCall(c) and + c.getTarget() = fcn and + hc = hashCons(c.getArgument(i).getFullyConverted()) and ( exists(HashCons head, HC_Alloc tail | list = HC_AllocArgCons(fcn, head, i - 1, tail, aligned) and - mk_AllocArgCons(fcn, head, i - 1, tail, aligned, fc) and + mk_AllocArgCons(fcn, head, i - 1, tail, aligned, c) and ( aligned = true and i > 2 @@ -735,6 +779,34 @@ private predicate mk_SizeofExpr(HashCons child, SizeofExprOperator e) { child = hashCons(e.getAChild()) } +private predicate analyzableUuidofOperator(UuidofOperator e) { + strictcount(e.getTypeOperand()) = 1 +} + +private predicate mk_UuidofOperator(Type t, UuidofOperator e) { + analyzableUuidofOperator(e) and + t = e.getTypeOperand() +} + +private predicate analyzableTypeidType(TypeidOperator e) { + strictcount(e.getAChild()) = 0 +} + +private predicate mk_TypeidType(Type t, TypeidOperator e) { + analyzableTypeidType(e) and + t = e.getResultType() +} + +private predicate analyzableTypeidExpr(Expr e) { + e instanceof TypeidOperator and + strictcount(e.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_TypeidExpr(HashCons child, TypeidOperator e) { + analyzableTypeidExpr(e) and + child = hashCons(e.getAChild()) +} + private predicate analyzableAlignofType(AlignofTypeOperator e) { strictcount(e.getType().getUnspecifiedType()) = 1 and strictcount(e.getTypeOperand()) = 1 @@ -851,6 +923,30 @@ private predicate mk_ReThrowExpr(ReThrowExpr te) { any() } +private predicate analyzableConditionalExpr(ConditionalExpr ce) { + strictcount(ce.getCondition().getFullyConverted()) = 1 and + strictcount(ce.getThen().getFullyConverted()) = 1 and + strictcount(ce.getElse().getFullyConverted()) = 1 +} + +private predicate mk_ConditionalExpr(HashCons cond, HashCons trueHc, HashCons falseHc, + ConditionalExpr ce) { + analyzableConditionalExpr(ce) and + cond.getAnExpr() = ce.getCondition() and + trueHc.getAnExpr() = ce.getThen() and + falseHc.getAnExpr() = ce.getElse() +} + +private predicate analyzableNoExceptExpr(NoExceptExpr nee) { + strictcount(nee.getAChild().getFullyConverted()) = 1 +} + +private predicate mk_NoExceptExpr(HashCons child, NoExceptExpr nee) { + analyzableNoExceptExpr(nee) and + nee.getExpr() = child.getAnExpr().getFullyConverted() +} + + /** Gets the hash-cons of expression `e`. */ cached HashCons hashCons(Expr e) { exists (int val, Type t @@ -914,6 +1010,11 @@ cached HashCons hashCons(Expr e) { result = HC_NonmemberFunctionCall(fcn, args) ) or + exists(HashCons hc, HC_Args args + | mk_ExprCall(hc, args, e) and + result = HC_ExprCall(hc, args) + ) + or exists(Function fcn, HashCons qual, HC_Args args | mk_MemberFunctionCall(fcn, qual, args, e) and result = HC_MemberFunctionCall(fcn, qual, args) @@ -940,6 +1041,16 @@ cached HashCons hashCons(Expr e) { ) or exists(Type t + | mk_TypeidType(t, e) and + result = HC_TypeidType(t) + ) + or + exists(HashCons child + | mk_TypeidExpr(child, e) and + result = HC_TypeidExpr(child) + ) + or + exists(Type t | mk_AlignofType(t, e) and result = HC_AlignofType(t) ) @@ -979,6 +1090,11 @@ cached HashCons hashCons(Expr e) { result = HC_ReThrowExpr() ) or + exists(HashCons cond, HashCons thenHC, HashCons elseHC + | mk_ConditionalExpr(cond, thenHC, elseHC, e) and + result = HC_ConditionalExpr(cond, thenHC, elseHC) + ) + or ( mk_Nullptr(e) and result = HC_Nullptr() @@ -1011,16 +1127,22 @@ predicate analyzableExpr(Expr e, string kind) { (analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or (analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or (analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") or + (analyzableExprCall(e) and kind = "ExprCall") or (analyzableNewExpr(e) and kind = "NewExpr") or (analyzableNewArrayExpr(e) and kind = "NewArrayExpr") or (analyzableSizeofType(e) and kind = "SizeofTypeOperator") or (analyzableSizeofExpr(e) and kind = "SizeofExprOperator") or (analyzableAlignofType(e) and kind = "AlignofTypeOperator") or (analyzableAlignofExpr(e) and kind = "AlignofExprOperator") or + (analyzableUuidofOperator(e) and kind = "UuidofOperator") or + (analyzableTypeidType(e) and kind = "TypeidType") or + (analyzableTypeidExpr(e) and kind = "TypeidExpr") or (analyzableClassAggregateLiteral(e) and kind = "ClassAggregateLiteral") or (analyzableArrayAggregateLiteral(e) and kind = "ArrayAggregateLiteral") or (analyzableDeleteExpr(e) and kind = "DeleteExpr") or (analyzableDeleteArrayExpr(e) and kind = "DeleteArrayExpr") or (analyzableThrowExpr(e) and kind = "ThrowExpr") or - (analyzableReThrowExpr(e) and kind = "ReThrowExpr") + (analyzableReThrowExpr(e) and kind = "ReThrowExpr") or + (analyzableConditionalExpr(e) and kind = "ConditionalExpr") or + (analyzableNoExceptExpr(e) and kind = "NoExceptExpr") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 0680e7d8f2c7..b985d73d559e 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -111,3 +111,13 @@ | test.cpp:311:5:311:5 | 0 | 311:c5-c5 312:c5-c5 315:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 | | test.cpp:313:3:313:6 | access to array | 313:c3-c6 314:c3-c6 | | test.cpp:315:3:315:3 | y | 315:c3-c3 316:c3-c3 | +| test.cpp:323:3:323:11 | test_18_p | 323:c3-c11 324:c3-c11 | +| test.cpp:323:3:323:13 | call to expression | 323:c3-c13 324:c3-c13 | +| test.cpp:327:3:327:11 | test_19_p | 327:c3-c11 328:c3-c11 329:c3-c11 | +| test.cpp:327:3:327:17 | call to expression | 327:c3-c17 328:c3-c17 | +| test.cpp:327:13:327:13 | x | 327:c13-c13 328:c13-c13 329:c16-c16 | +| test.cpp:327:16:327:16 | y | 327:c16-c16 328:c16-c16 329:c13-c13 | +| test.cpp:333:3:333:8 | ... == ... | 333:c3-c8 334:c3-c8 336:c3-c8 | +| test.cpp:333:3:333:16 | ... ? ... : ... | 333:c3-c16 334:c3-c16 | +| test.cpp:333:12:333:12 | x | 333:c12-c12 333:c3-c3 334:c12-c12 334:c3-c3 335:c12-c12 335:c8-c8 336:c16-c16 336:c3-c3 | +| test.cpp:333:16:333:16 | y | 333:c16-c16 333:c8-c8 334:c16-c16 334:c8-c8 335:c16-c16 335:c3-c3 336:c12-c12 336:c8-c8 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 5dcbf8489210..40635ed268c7 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -315,3 +315,23 @@ void test19(int *x, int *y) { y[0]; y[1]; } + +void test20(int *x, int *y) { + void (*test_18_p)() = &test18; + void (*test_17_p)() = &test17; + void (*test_19_p)(int *, int *) = &test19; + test_18_p(); + test_18_p(); + test_17_p(); + + test_19_p(x, y); + test_19_p(x, y); + test_19_p(y, x); +} + +void test21(int x, int y) { + x == y ? x : y; + x == y ? x : y; + y == x ? x : y; + x == y ? y : x; +} From 246ae2d7e88fc0d427e36a3a4af62f971a16d048 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 30 Aug 2018 11:43:02 -0700 Subject: [PATCH 028/110] C++: fix performance of argument hash-consing --- .../code/cpp/valuenumbering/HashCons.qll | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 0765ff92cf92..d3542bcafe73 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -496,8 +496,7 @@ private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionC analyzableNonmemberFunctionCall(fc) and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) + mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc) ) or fc.getNumberOfArguments() = 0 and @@ -517,8 +516,7 @@ private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) { hc.getAnExpr() = ec.getExpr() and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(head, ec.getNumberOfArguments() - 1, tail) and - mk_ArgCons(head, ec.getNumberOfArguments() - 1, tail, ec) + mk_ArgConsInner(head, tail, ec.getNumberOfArguments() - 1, args, ec) ) or ec.getNumberOfArguments() = 0 and @@ -547,8 +545,7 @@ private predicate mk_MemberFunctionCall( hashCons(fc.getQualifier().getFullyConverted()) = qual and ( exists(HashCons head, HC_Args tail | - args = HC_ArgCons(head, fc.getNumberOfArguments() - 1, tail) and - mk_ArgCons(head, fc.getNumberOfArguments() - 1, tail, fc) + mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc) ) or fc.getNumberOfArguments() = 0 and @@ -568,13 +565,12 @@ private predicate analyzableCall(Call c) { * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. */ -private predicate mk_ArgCons(HashCons hc, int i, HC_Args list,Call c) { +private predicate mk_ArgCons(HashCons hc, int i, HC_Args list, Call c) { analyzableCall(c) and hc = hashCons(c.getArgument(i).getFullyConverted()) and ( exists(HashCons head, HC_Args tail | - list = HC_ArgCons(head, i - 1, tail) and - mk_ArgCons(head, i - 1, tail, c) and + mk_ArgConsInner(head, tail, i-1, list, c) and i > 0 ) or @@ -583,6 +579,12 @@ private predicate mk_ArgCons(HashCons hc, int i, HC_Args list,Call c) { ) } +// avoid a join ordering issue +pragma[noopt] +private predicate mk_ArgConsInner(HashCons head, HC_Args tail, int i, HC_Args list, Call c) { + list = HC_ArgCons(head, i, tail) and + mk_ArgCons(head, i, tail, c) +} /** * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons From fa9eeea302a6fa77b25149bcf59ee4e5d72dd10f Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 29 Aug 2018 12:33:50 -0700 Subject: [PATCH 029/110] C++: remove implicit this handling in HashCons --- .../code/cpp/valuenumbering/HashCons.qll | 55 ++++++------------- 1 file changed, 16 insertions(+), 39 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index d3542bcafe73..82d757c5acaa 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -56,20 +56,23 @@ private cached newtype HCBase = } or HC_FieldAccess(HashCons s, Field f) { - mk_DotFieldAccess(s,f,_) or - mk_PointerFieldAccess_with_deref(s,f,_) or - mk_ImplicitThisFieldAccess_with_deref(s,f,_) + mk_DotFieldAccess(s,f,_) } or HC_Deref(HashCons p) { - mk_Deref(p,_) or - mk_PointerFieldAccess(p,_,_) or - mk_ImplicitThisFieldAccess_with_qualifier(p,_,_) + mk_Deref(p,_) + } + or + HC_PointerFieldAccess(HashCons qual, Field target) { + mk_PointerFieldAccess(qual, target, _) } or HC_ThisExpr(Function fcn) { - mk_ThisExpr(fcn,_) or - mk_ImplicitThisFieldAccess(fcn,_,_) + mk_ThisExpr(fcn,_) + } + or + HC_ImplicitThisFieldAccess (Function fcn, Field target){ + mk_ImplicitThisFieldAccess(fcn, target, _) } or HC_Conversion(Type t, HashCons child) { mk_Conversion(t, child, _) } @@ -368,17 +371,6 @@ private predicate mk_PointerFieldAccess( qualifier = hashCons(access.getQualifier().getFullyConverted()) } -/* - * `obj->field` is equivalent to `(*obj).field`, so we need to wrap an - * extra `HC_Deref` around the qualifier. - */ -private predicate mk_PointerFieldAccess_with_deref (HashCons new_qualifier, Field target, - PointerFieldAccess access) { - exists (HashCons qualifier - | mk_PointerFieldAccess(qualifier, target, access) and - new_qualifier = HC_Deref(qualifier)) -} - private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) { strictcount (access.getTarget()) = 1 and strictcount (access.getEnclosingFunction()) = 1 @@ -391,21 +383,6 @@ private predicate mk_ImplicitThisFieldAccess(Function fcn, Field target, fcn = access.getEnclosingFunction() } -private predicate mk_ImplicitThisFieldAccess_with_qualifier( HashCons qualifier, Field target, - ImplicitThisFieldAccess access) { - exists (Function fcn - | mk_ImplicitThisFieldAccess(fcn, target, access) and - qualifier = HC_ThisExpr(fcn)) -} - -private predicate mk_ImplicitThisFieldAccess_with_deref(HashCons new_qualifier, Field target, - ImplicitThisFieldAccess access) { - exists (HashCons qualifier - | mk_ImplicitThisFieldAccess_with_qualifier( - qualifier, target, access) and - new_qualifier = HC_Deref(qualifier)) -} - private predicate analyzableVariable(VariableAccess access) { not (access instanceof FieldAccess) and strictcount (access.getTarget()) = 1 @@ -976,12 +953,12 @@ cached HashCons hashCons(Expr e) { result = HC_FieldAccess(qualifier, target)) or exists (HashCons qualifier, Field target - | mk_PointerFieldAccess_with_deref(qualifier, target, e) and - result = HC_FieldAccess(qualifier, target)) + | mk_PointerFieldAccess(qualifier, target, e) and + result = HC_PointerFieldAccess(qualifier, target)) or - exists (HashCons qualifier, Field target - | mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and - result = HC_FieldAccess(qualifier, target)) + exists (Function fcn, Field target + | mk_ImplicitThisFieldAccess(fcn, target, e) and + result = HC_ImplicitThisFieldAccess(fcn, target)) or exists (Function fcn | mk_ThisExpr(fcn, e) and From 9f476e585acb80e6225a75bd2fb21b86050fb209 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 30 Aug 2018 13:22:51 -0700 Subject: [PATCH 030/110] C++: Simplify some code --- .../code/cpp/valuenumbering/HashCons.qll | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 82d757c5acaa..7a7b33ade608 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -197,7 +197,7 @@ private newtype HC_Fields = private newtype HC_Array = HC_EmptyArray(Type t) { exists(ArrayAggregateLiteral aal | - aal.getType() = t + aal.getType().getUnspecifiedType() = t ) } or @@ -602,20 +602,13 @@ private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) { } private predicate mk_HasAlign(HashCons hc, NewOrNewArrayExpr new) { - hc = hashCons(new.getAlignmentArgument()) + hc = hashCons(new.getAlignmentArgument().getFullyConverted()) } private predicate analyzableNewExpr(NewExpr new) { strictcount(new.getAllocatedType()) = 1 and - ( - not exists(new.getAllocatorCall()) - or - strictcount(new.getAllocatorCall()) = 1 - ) and ( - not exists(new.getInitializer()) - or - strictcount(new.getInitializer()) = 1 - ) + count(new.getAllocatorCall().getFullyConverted()) <= 1 and + count(new.getInitializer().getFullyConverted()) <= 1 } private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned, @@ -623,7 +616,7 @@ private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align alig analyzableNewExpr(new) and t = new.getAllocatedType() and ( - align = HC_HasAlign(hashCons(new.getAlignmentArgument())) and + align = HC_HasAlign(hashCons(new.getAlignmentArgument().getFullyConverted())) and aligned = true or not new.hasAlignedAllocation() and @@ -664,16 +657,8 @@ private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align alig private predicate analyzableNewArrayExpr(NewArrayExpr new) { strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and - strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and - ( - not exists(new.getAllocatorCall()) - or - strictcount(new.getAllocatorCall().getFullyConverted()) = 1 - ) and ( - not exists(new.getInitializer()) - or - strictcount(new.getInitializer().getFullyConverted()) = 1 - ) + count(new.getAllocatorCall().getFullyConverted()) <= 1 and + count(new.getInitializer().getFullyConverted()) <= 1 } private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, @@ -681,7 +666,7 @@ private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align analyzableNewArrayExpr(new) and t = new.getAllocatedType() and ( - align = HC_HasAlign(hashCons(new.getAlignmentArgument())) and + align = HC_HasAlign(hashCons(new.getAlignmentArgument().getFullyConverted())) and aligned = true or not new.hasAlignedAllocation() and From c42ecfe8f9605f55b8b6cc912df36deaf7a1502c Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 30 Aug 2018 14:48:57 -0700 Subject: [PATCH 031/110] C++: Simplify HashCons for new and handle extents --- .../code/cpp/valuenumbering/HashCons.qll | 208 +++++++----------- .../valuenumbering/HashCons/HashCons.expected | 93 ++++---- .../valuenumbering/HashCons/test.cpp | 15 +- 3 files changed, 140 insertions(+), 176 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 7a7b33ade608..e3ac972a5692 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -98,11 +98,17 @@ private cached newtype HCBase = mk_MemberFunctionCall(trg, qual, args, _) } or - HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align) { - mk_NewExpr(t, alloc, init, align, _, _) - } or - HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align) { - mk_NewArrayExpr(t, alloc, init, align, _, _) + // Hack to get around argument 0 of allocator calls being an error expression + HC_AllocatorArgZero(Type t) { + mk_AllocatorArgZero(t, _) + } + or + HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init) { + mk_NewExpr(t, alloc, init, _) + } + or + HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init) { + mk_NewArrayExpr(t, alloc, extent, init, _) } or HC_SizeofType(Type t) {mk_SizeofType(t, _)} @@ -147,30 +153,27 @@ private cached newtype HCBase = // given a unique number based on the expression itself. HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) } -/** Used to implement hash-consing of `new` placement argument lists */ -private newtype HC_Alloc = - HC_EmptyAllocArgs(Function fcn) { - exists(NewOrNewArrayExpr n | - n.getAllocator() = fcn - ) - } - or HC_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned) { - mk_AllocArgCons(fcn, hc, i, list, aligned, _) - } - or - HC_NoAlloc() - /** Used to implement optional init on `new` expressions */ private newtype HC_Init = HC_NoInit() or HC_HasInit(HashCons hc) {mk_HasInit(hc, _)} -private newtype HC_Align = - HC_NoAlign() +/** + * Used to implement optional allocator call on `new` expressions + */ +private newtype HC_Alloc = + HC_NoAlloc() or - HC_HasAlign(HashCons hc) {mk_HasAlign(hc, _)} - + HC_HasAlloc(HashCons hc) {mk_HasAlloc(hc, _)} + +/** + * Used to implement optional extent expression on `new[]` exprtessions + */ +private newtype HC_Extent = + HC_NoExtent() + or + HC_HasExtent(HashCons hc) {mk_HasExtent(hc, _)} /** Used to implement hash-consing of argument lists */ private newtype HC_Args = HC_EmptyArgs() { @@ -260,6 +263,7 @@ class HashCons extends HCBase { if this instanceof HC_ExprCall then result = "ExprCall" else if this instanceof HC_ConditionalExpr then result = "ConditionalExpr" else if this instanceof HC_NoExceptExpr then result = "NoExceptExpr" else + if this instanceof HC_AllocatorArgZero then result = "AllocatorArgZero" else result = "error" } @@ -563,94 +567,57 @@ private predicate mk_ArgConsInner(HashCons head, HC_Args tail, int i, HC_Args li mk_ArgCons(head, i, tail, c) } -/** - * Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons - * `list`, and `fc`'s argument at index `i` has hash-cons `hc`. +/* + * The 0th argument of an allocator call in a new expression is always an error expression; + * this works around it */ -private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, - Call c) { - analyzableCall(c) and - c.getTarget() = fcn and - hc = hashCons(c.getArgument(i).getFullyConverted()) and - ( - exists(HashCons head, HC_Alloc tail | - list = HC_AllocArgCons(fcn, head, i - 1, tail, aligned) and - mk_AllocArgCons(fcn, head, i - 1, tail, aligned, c) and - ( - aligned = true and - i > 2 - or - aligned = false and - i > 1 - ) - ) - or - ( - aligned = true and - i = 2 - or - aligned = false and - i = 1 - ) and - list = HC_EmptyAllocArgs(fcn) +private predicate analyzableAllocatorArgZero(ErrorExpr e) { + exists(NewOrNewArrayExpr new | + new.getAllocatorCall().getChild(0) = e + ) +} + +private predicate mk_AllocatorArgZero(Type t, ErrorExpr e) { + exists(NewOrNewArrayExpr new | + new.getAllocatorCall().getChild(0) = e and + t = new.getType().getUnspecifiedType() ) } private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) { - hc = hashCons(new.(NewExpr).getInitializer()) or - hc = hashCons(new.(NewArrayExpr).getInitializer()) + hc = hashCons(new.(NewExpr).getInitializer().getFullyConverted()) or + hc = hashCons(new.(NewArrayExpr).getInitializer().getFullyConverted()) } -private predicate mk_HasAlign(HashCons hc, NewOrNewArrayExpr new) { - hc = hashCons(new.getAlignmentArgument().getFullyConverted()) +private predicate mk_HasAlloc(HashCons hc, NewOrNewArrayExpr new) { + hc = hashCons(new.(NewExpr).getAllocatorCall().getFullyConverted()) or + hc = hashCons(new.(NewArrayExpr).getAllocatorCall().getFullyConverted()) +} + +private predicate mk_HasExtent(HashCons hc, NewArrayExpr new) { + hc = hashCons(new.(NewArrayExpr).getExtent().getFullyConverted()) } private predicate analyzableNewExpr(NewExpr new) { - strictcount(new.getAllocatedType()) = 1 and + strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and count(new.getAllocatorCall().getFullyConverted()) <= 1 and count(new.getInitializer().getFullyConverted()) <= 1 } -private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned, - NewExpr new) { +private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, NewExpr new) { analyzableNewExpr(new) and - t = new.getAllocatedType() and + t = new.getAllocatedType().getUnspecifiedType() and ( - align = HC_HasAlign(hashCons(new.getAlignmentArgument().getFullyConverted())) and - aligned = true - or - not new.hasAlignedAllocation() and - align = HC_NoAlign() and - aligned = false - ) - and - ( - exists(FunctionCall fc, HashCons head, HC_Alloc tail | - fc = new.getAllocatorCall() and - alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and - mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc) - ) - or - exists(FunctionCall fc | - fc = new.getAllocatorCall() and - ( - aligned = true and - fc.getNumberOfArguments() = 2 - or - aligned = false and - fc.getNumberOfArguments() = 1 - ) and - alloc = HC_EmptyAllocArgs(fc.getTarget()) - ) + alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted())) or - not exists(new.getAllocatorCall()) and + not exists(new.getAllocatorCall().getFullyConverted()) and alloc = HC_NoAlloc() ) and ( - init = HC_HasInit(hashCons(new.getInitializer())) + init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted())) or - not exists(new.getInitializer()) and + not exists(new.getInitializer().getFullyConverted()) and init = HC_NoInit() ) } @@ -658,50 +625,34 @@ private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align alig private predicate analyzableNewArrayExpr(NewArrayExpr new) { strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and count(new.getAllocatorCall().getFullyConverted()) <= 1 and - count(new.getInitializer().getFullyConverted()) <= 1 + count(new.getInitializer().getFullyConverted()) <= 1 and + count(new.(NewArrayExpr).getExtent().getFullyConverted()) <= 1 } -private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, - boolean aligned, NewArrayExpr new) { +private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init, NewArrayExpr new) { analyzableNewArrayExpr(new) and t = new.getAllocatedType() and + ( - align = HC_HasAlign(hashCons(new.getAlignmentArgument().getFullyConverted())) and - aligned = true + alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted())) or - not new.hasAlignedAllocation() and - align = HC_NoAlign() and - aligned = false + not exists(new.getAllocatorCall().getFullyConverted()) and + alloc = HC_NoAlloc() ) and ( - exists(FunctionCall fc, HashCons head, HC_Alloc tail | - fc = new.getAllocatorCall() and - alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and - mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc) - ) - or - exists(FunctionCall fc | - fc = new.getAllocatorCall() and - ( - aligned = true and - fc.getNumberOfArguments() = 2 - or - aligned = false and - fc.getNumberOfArguments() = 1 - ) and - alloc = HC_EmptyAllocArgs(fc.getTarget()) - ) + init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted())) or - not exists(new.getAllocatorCall()) and - alloc = HC_NoAlloc() + not exists(new.getInitializer().getFullyConverted()) and + init = HC_NoInit() ) and + ( - init = HC_HasInit(hashCons(new.getInitializer())) + extent = HC_HasExtent(hashCons(new.getExtent().getFullyConverted())) or - not exists(new.getInitializer()) and - init = HC_NoInit() + not exists(new.getExtent().getFullyConverted()) and + extent = HC_NoExtent() ) } @@ -984,14 +935,20 @@ cached HashCons hashCons(Expr e) { result = HC_MemberFunctionCall(fcn, qual, args) ) or - exists(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned - | mk_NewExpr(t, alloc, init, align, aligned, e) and - result = HC_NewExpr(t, alloc, init, align) + // works around an extractor issue class + exists(Type t + | mk_AllocatorArgZero(t, e) and + result = HC_AllocatorArgZero(t) + ) + or + exists(Type t, HC_Alloc alloc, HC_Init init + | mk_NewExpr(t, alloc, init, e) and + result = HC_NewExpr(t, alloc, init) ) or - exists(Type t, HC_Alloc alloc, HC_Init init, HC_Align align, boolean aligned - | mk_NewArrayExpr(t, alloc, init, align, aligned, e) and - result = HC_NewArrayExpr(t, alloc, init, align) + exists(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init + | mk_NewArrayExpr(t, alloc, extent, init, e) and + result = HC_NewArrayExpr(t, alloc, extent, init) ) or exists(Type t @@ -1108,5 +1065,6 @@ predicate analyzableExpr(Expr e, string kind) { (analyzableThrowExpr(e) and kind = "ThrowExpr") or (analyzableReThrowExpr(e) and kind = "ReThrowExpr") or (analyzableConditionalExpr(e) and kind = "ConditionalExpr") or - (analyzableNoExceptExpr(e) and kind = "NoExceptExpr") + (analyzableNoExceptExpr(e) and kind = "NoExceptExpr") or + (analyzableAllocatorArgZero(e) and kind = "AllocatorArgZero") } diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index b985d73d559e..48adfedf87ec 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -37,9 +37,9 @@ | test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 271:c28-c28 272:c28-c28 274:c19-c19 274:c22-c22 288:c5-c5 292:c5-c5 297:c5-c5 302:c9-c9 303:c9-c9 313:c5-c5 314:c5-c5 316:c5-c5 | +| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 239:c11-c11 240:c11-c11 263:c28-c28 264:c28-c28 266:c19-c19 266:c22-c22 285:c5-c5 289:c5-c5 294:c5-c5 299:c9-c9 300:c9-c9 310:c5-c5 311:c5-c5 313:c5-c5 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 271:c24-c24 271:c31-c31 272:c24-c24 272:c31-c31 273:c24-c24 274:c15-c15 275:c15-c15 275:c19-c19 275:c22-c22 277:c15-c15 278:c15-c15 289:c5-c5 293:c5-c5 296:c5-c5 304:c9-c9 305:c9-c9 | +| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 241:c11-c11 263:c24-c24 263:c31-c31 264:c24-c24 264:c31-c31 265:c24-c24 266:c15-c15 267:c15-c15 267:c19-c19 267:c22-c22 269:c15-c15 270:c15-c15 286:c5-c5 290:c5-c5 293:c5-c5 301:c9-c9 302:c9-c9 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | | test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | | test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | @@ -73,7 +73,7 @@ | test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 | | test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | | test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 | -| test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 253:c25-c35 254:c25-c35 | +| test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 245:c25-c35 246:c25-c35 | | test.cpp:206:25:206:43 | alignof(int_holder) | 206:c25-c43 206:c3-c21 | | test.cpp:208:27:208:27 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 | | test.cpp:209:10:209:15 | holder | 209:c10-c15 209:c29-c34 | @@ -82,42 +82,51 @@ | test.cpp:209:22:209:37 | sizeof() | 209:c22-c37 209:c3-c18 | | test.cpp:210:10:210:11 | (...) | 210:c10-c11 211:c11-c12 211:c24-c25 | | test.cpp:211:16:211:25 | alignof() | 211:c16-c25 211:c3-c12 | -| test.cpp:247:3:247:12 | new | 247:c3-c12 248:c3-c12 | -| test.cpp:253:16:253:36 | new[] | 253:c16-c36 254:c16-c36 | -| test.cpp:256:3:256:28 | delete | 256:c3-c28 257:c3-c28 | -| test.cpp:256:10:256:28 | new | 256:c10-c28 257:c10-c28 | -| test.cpp:256:14:256:17 | (void *)... | 256:c14-c17 257:c14-c17 260:c18-c21 261:c20-c23 263:c11-c14 | -| test.cpp:256:14:256:17 | ptr1 | 256:c14-c17 257:c14-c17 260:c18-c21 261:c20-c23 263:c11-c14 | -| test.cpp:258:14:258:17 | (void *)... | 258:c14-c17 262:c11-c14 | -| test.cpp:258:14:258:17 | ptr2 | 258:c14-c17 262:c11-c14 | -| test.cpp:260:10:260:32 | new | 260:c10-c32 261:c12-c34 | -| test.cpp:260:14:260:15 | 32 | 260:c14-c15 261:c16-c17 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c16-c17 272:c16-c17 273:c16-c17 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | -| test.cpp:260:14:260:15 | (size_t)... | 260:c14-c15 261:c16-c17 262:c7-c8 265:c7-c8 266:c7-c8 268:c7-c8 269:c7-c8 271:c16-c17 272:c16-c17 273:c16-c17 274:c7-c8 275:c7-c8 277:c7-c8 278:c7-c8 | -| test.cpp:265:3:265:19 | new | 265:c3-c19 266:c3-c19 | -| test.cpp:268:3:268:23 | new[] | 268:c3-c23 269:c3-c23 | -| test.cpp:268:21:268:22 | 10 | 268:c21-c22 269:c21-c22 92:c15-c16 | -| test.cpp:271:3:271:32 | delete[] | 271:c3-c32 272:c3-c32 | -| test.cpp:271:12:271:32 | new[] | 271:c12-c32 272:c12-c32 | -| test.cpp:271:12:271:32 | {...} | 271:c12-c32 272:c12-c32 | -| test.cpp:273:28:273:28 | 3 | 273:c28-c28 35:c16-c16 | -| test.cpp:277:3:277:19 | new[] | 277:c3-c19 278:c3-c19 | -| test.cpp:277:3:277:19 | {...} | 277:c3-c19 278:c3-c19 | -| test.cpp:287:15:290:3 | {...} | 287:c15-c3 291:c15-c3 | -| test.cpp:302:3:302:9 | throw ... | 302:c3-c9 303:c3-c9 | -| test.cpp:304:3:304:9 | throw ... | 304:c3-c9 305:c3-c9 | -| test.cpp:306:3:306:7 | re-throw exception | 306:c3-c7 307:c3-c7 | -| test.cpp:311:3:311:3 | x | 311:c3-c3 312:c3-c3 313:c3-c3 314:c3-c3 | -| test.cpp:311:3:311:6 | access to array | 311:c3-c6 312:c3-c6 | -| test.cpp:311:5:311:5 | 0 | 311:c5-c5 312:c5-c5 315:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 | -| test.cpp:313:3:313:6 | access to array | 313:c3-c6 314:c3-c6 | -| test.cpp:315:3:315:3 | y | 315:c3-c3 316:c3-c3 | -| test.cpp:323:3:323:11 | test_18_p | 323:c3-c11 324:c3-c11 | -| test.cpp:323:3:323:13 | call to expression | 323:c3-c13 324:c3-c13 | -| test.cpp:327:3:327:11 | test_19_p | 327:c3-c11 328:c3-c11 329:c3-c11 | -| test.cpp:327:3:327:17 | call to expression | 327:c3-c17 328:c3-c17 | -| test.cpp:327:13:327:13 | x | 327:c13-c13 328:c13-c13 329:c16-c16 | -| test.cpp:327:16:327:16 | y | 327:c16-c16 328:c16-c16 329:c13-c13 | -| test.cpp:333:3:333:8 | ... == ... | 333:c3-c8 334:c3-c8 336:c3-c8 | -| test.cpp:333:3:333:16 | ... ? ... : ... | 333:c3-c16 334:c3-c16 | -| test.cpp:333:12:333:12 | x | 333:c12-c12 333:c3-c3 334:c12-c12 334:c3-c3 335:c12-c12 335:c8-c8 336:c16-c16 336:c3-c3 | -| test.cpp:333:16:333:16 | y | 333:c16-c16 333:c8-c8 334:c16-c16 334:c8-c8 335:c16-c16 335:c3-c3 336:c12-c12 336:c8-c8 | +| test.cpp:239:3:239:12 | new | 239:c3-c12 240:c3-c12 | +| test.cpp:245:16:245:36 | new[] | 245:c16-c36 246:c16-c36 | +| test.cpp:248:3:248:28 | delete | 248:c3-c28 249:c3-c28 | +| test.cpp:248:10:248:28 | | 248:c10-c28 249:c10-c28 250:c10-c28 252:c10-c32 253:c12-c34 254:c3-c25 255:c3-c25 257:c3-c19 258:c3-c19 260:c3-c23 261:c3-c23 | +| test.cpp:248:10:248:28 | call to operator new | 248:c10-c28 249:c10-c28 | +| test.cpp:248:10:248:28 | new | 248:c10-c28 249:c10-c28 | +| test.cpp:248:14:248:17 | (void *)... | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | +| test.cpp:248:14:248:17 | ptr1 | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | +| test.cpp:250:14:250:17 | (void *)... | 250:c14-c17 254:c11-c14 | +| test.cpp:250:14:250:17 | ptr2 | 250:c14-c17 254:c11-c14 | +| test.cpp:252:10:252:32 | call to operator new | 252:c10-c32 253:c12-c34 | +| test.cpp:252:10:252:32 | new | 252:c10-c32 253:c12-c34 | +| test.cpp:252:14:252:15 | 32 | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | +| test.cpp:252:14:252:15 | (size_t)... | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | +| test.cpp:257:3:257:19 | call to operator new | 257:c3-c19 258:c3-c19 | +| test.cpp:257:3:257:19 | new | 257:c3-c19 258:c3-c19 | +| test.cpp:260:3:260:23 | call to operator new[] | 260:c3-c23 261:c3-c23 | +| test.cpp:260:3:260:23 | new[] | 260:c3-c23 261:c3-c23 | +| test.cpp:260:21:260:22 | 10 | 260:c21-c22 261:c21-c22 92:c15-c16 | +| test.cpp:263:3:263:32 | delete[] | 263:c3-c32 264:c3-c32 | +| test.cpp:263:12:263:32 | | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | +| test.cpp:263:12:263:32 | call to operator new[] | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | +| test.cpp:263:12:263:32 | new[] | 263:c12-c32 264:c12-c32 | +| test.cpp:263:12:263:32 | {...} | 263:c12-c32 264:c12-c32 | +| test.cpp:265:28:265:28 | 3 | 265:c28-c28 271:c15-c15 35:c16-c16 | +| test.cpp:269:3:269:19 | new[] | 269:c3-c19 270:c3-c19 | +| test.cpp:269:3:269:19 | {...} | 269:c3-c19 270:c3-c19 | +| test.cpp:273:3:273:12 | new[] | 273:c3-c12 274:c3-c12 | +| test.cpp:273:11:273:11 | x | 273:c11-c11 274:c11-c11 | +| test.cpp:284:15:287:3 | {...} | 284:c15-c3 288:c15-c3 | +| test.cpp:299:3:299:9 | throw ... | 299:c3-c9 300:c3-c9 | +| test.cpp:301:3:301:9 | throw ... | 301:c3-c9 302:c3-c9 | +| test.cpp:303:3:303:7 | re-throw exception | 303:c3-c7 304:c3-c7 | +| test.cpp:308:3:308:3 | x | 308:c3-c3 309:c3-c3 310:c3-c3 311:c3-c3 | +| test.cpp:308:3:308:6 | access to array | 308:c3-c6 309:c3-c6 | +| test.cpp:308:5:308:5 | 0 | 308:c5-c5 309:c5-c5 312:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 | +| test.cpp:310:3:310:6 | access to array | 310:c3-c6 311:c3-c6 | +| test.cpp:312:3:312:3 | y | 312:c3-c3 313:c3-c3 | +| test.cpp:320:3:320:11 | test_18_p | 320:c3-c11 321:c3-c11 | +| test.cpp:320:3:320:13 | call to expression | 320:c3-c13 321:c3-c13 | +| test.cpp:324:3:324:11 | test_19_p | 324:c3-c11 325:c3-c11 326:c3-c11 | +| test.cpp:324:3:324:17 | call to expression | 324:c3-c17 325:c3-c17 | +| test.cpp:324:13:324:13 | x | 324:c13-c13 325:c13-c13 326:c16-c16 | +| test.cpp:324:16:324:16 | y | 324:c16-c16 325:c16-c16 326:c13-c13 | +| test.cpp:330:3:330:8 | ... == ... | 330:c3-c8 331:c3-c8 333:c3-c8 | +| test.cpp:330:3:330:16 | ... ? ... : ... | 330:c3-c16 331:c3-c16 | +| test.cpp:330:12:330:12 | x | 330:c12-c12 330:c3-c3 331:c12-c12 331:c3-c3 332:c12-c12 332:c8-c8 333:c16-c16 333:c3-c3 | +| test.cpp:330:16:330:16 | y | 330:c16-c16 330:c8-c8 331:c16-c16 331:c8-c8 332:c16-c16 332:c3-c3 333:c12-c12 333:c8-c8 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp index 40635ed268c7..56190e8af2ac 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -211,10 +211,6 @@ int test15(int x) { alignof(x) + alignof(x); } -static void *operator new(size_t size) { - return malloc(size); -} - static void *operator new(size_t size, void *placement) { return placement; } @@ -227,10 +223,6 @@ static void *operator new(size_t size, size_t alignment) { return malloc(size); } -static void *operator new[](size_t size) { - return malloc(size); -} - static void *operator new[](size_t size, void *placement) { return placement; } @@ -243,7 +235,7 @@ static void *operator new[](size_t size, size_t alignment) { return malloc(size); } -void test16() { +void test16(int y, int z) { new int(1); new int(1); new int(2); @@ -276,6 +268,11 @@ void test16() { new(32) int[2] {}; new(32) int[2] {}; + new(32) int[3] {}; + + new int[x]; + new int[x]; + new int[z]; } typedef struct point{ From 2d098fed98906ecfbe7dde5ad5ebfc3e0c30fcbf Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 30 Aug 2018 15:02:57 -0700 Subject: [PATCH 032/110] fix HashCons for typeid of type --- cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index e3ac972a5692..6aefb22f510e 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -704,7 +704,7 @@ private predicate mk_UuidofOperator(Type t, UuidofOperator e) { } private predicate analyzableTypeidType(TypeidOperator e) { - strictcount(e.getAChild()) = 0 + count(e.getAChild()) = 0 } private predicate mk_TypeidType(Type t, TypeidOperator e) { From bbafcd99414a3ac0494eae6e719f636a53222925 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 31 Aug 2018 08:34:43 -0700 Subject: [PATCH 033/110] C++: typeid and noexcept fixes in HashCons --- cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 6aefb22f510e..5494d656ac3e 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -704,7 +704,8 @@ private predicate mk_UuidofOperator(Type t, UuidofOperator e) { } private predicate analyzableTypeidType(TypeidOperator e) { - count(e.getAChild()) = 0 + count(e.getAChild()) = 0 and + strictcount(e.getResultType()) = 1 } private predicate mk_TypeidType(Type t, TypeidOperator e) { @@ -858,7 +859,7 @@ private predicate analyzableNoExceptExpr(NoExceptExpr nee) { private predicate mk_NoExceptExpr(HashCons child, NoExceptExpr nee) { analyzableNoExceptExpr(nee) and - nee.getExpr() = child.getAnExpr().getFullyConverted() + nee.getExpr().getFullyConverted() = child.getAnExpr() } From 166dba288b6c79a44ca741f93cd545a1f945dcc1 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 4 Sep 2018 12:26:05 -0700 Subject: [PATCH 034/110] C++: accept test output --- cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll | 7 +++---- .../valuenumbering/HashCons/HashCons.expected | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 5494d656ac3e..407ad5ff721f 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -748,14 +748,13 @@ private predicate mk_FieldCons(Class c, int i, Field f, HashCons hc, HC_Fields h cal.getType().getUnspecifiedType() = c and exists(Expr e | e = cal.getFieldExpr(f).getFullyConverted() and - e = cal.getChild(i).getFullyConverted() and + f.getInitializationOrder() = i and hc = hashCons(e) and ( exists(HashCons head, Field f2, HC_Fields tail | hcf = HC_FieldCons(c, i-1, f2, head, tail) and - cal.getChild(i-1).getFullyConverted() = cal.getFieldExpr(f2).getFullyConverted() and + f2.getInitializationOrder() = i-1 and mk_FieldCons(c, i-1, f2, head, tail, cal) - ) or i = 0 and @@ -777,7 +776,7 @@ private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregat c = cal.getType().getUnspecifiedType() and ( exists(HC_Fields tail, Expr e, Field f | - e = cal.getChild(cal.getNumChild() - 1).getFullyConverted() and + f.getInitializationOrder() = cal.getNumChild() - 1 and e = cal.getFieldExpr(f).getFullyConverted() and hcf = HC_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail) and mk_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail, cal) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 48adfedf87ec..0f92bbd658e9 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -111,7 +111,6 @@ | test.cpp:269:3:269:19 | {...} | 269:c3-c19 270:c3-c19 | | test.cpp:273:3:273:12 | new[] | 273:c3-c12 274:c3-c12 | | test.cpp:273:11:273:11 | x | 273:c11-c11 274:c11-c11 | -| test.cpp:284:15:287:3 | {...} | 284:c15-c3 288:c15-c3 | | test.cpp:299:3:299:9 | throw ... | 299:c3-c9 300:c3-c9 | | test.cpp:301:3:301:9 | throw ... | 301:c3-c9 302:c3-c9 | | test.cpp:303:3:303:7 | re-throw exception | 303:c3-c7 304:c3-c7 | From 990bfb4663b7deaf5b45cb61a8aacfea96587968 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 4 Sep 2018 13:47:42 -0700 Subject: [PATCH 035/110] C++: change note for HashCons library --- change-notes/1.18/analysis-cpp.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/change-notes/1.18/analysis-cpp.md b/change-notes/1.18/analysis-cpp.md index 00877a124623..7f3f28453874 100644 --- a/change-notes/1.18/analysis-cpp.md +++ b/change-notes/1.18/analysis-cpp.md @@ -31,10 +31,11 @@ | [Uncontrolled data in arithmetic expression] | More correct results | Increment / decrement / addition assignment / subtraction assignment operations are now understood as arithmetic operations in this query. | | [Use of extreme values in arithmetic expression] | More correct results | Increment / decrement / addition assignment / subtraction assignment operations are now understood as arithmetic operations in this query. | | [Use of extreme values in arithmetic expression] | Fewer false positives | The query now considers whether a particular expression might cause an overflow of minimum or maximum values only. | - + ## Changes to QL libraries * Fixes for aggregate initializers using designators: * `ClassAggregateLiteral.getFieldExpr()` previously assumed initializer expressions appeared in the same order as the declaration order of the fields, causing it to associate the expressions with the wrong fields when using designated initializers. This has been fixed. * `ArrayAggregateLiteral.getElementExpr()` previously assumed initializer expressions appeared in the same order as the corresponding array elements, causing it to associate the expressions with the wrong array elements when using designated initializers. This has been fixed. * `Element.getEnclosingElement()` no longer includes macro accesses in its results. To explore parents and children of macro accesses, use the relevant member predicates on `MacroAccess` or `MacroInvocation`. +* Added a hash consing library for structural comparison of expressions. From fb8ad9387d5fe3aad8f2b4561f5a0d987fff23cc Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 6 Sep 2018 12:32:37 -0700 Subject: [PATCH 036/110] C++: Uniqueness fixes for HashCons --- .../code/cpp/valuenumbering/HashCons.qll | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 407ad5ff721f..7f468aa2da5e 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -255,7 +255,7 @@ class HashCons extends HCBase { if this instanceof HC_TypeidType then result = "TypeidType" else if this instanceof HC_TypeidExpr then result = "TypeidExpr" else if this instanceof HC_ArrayAggregateLiteral then result = "ArrayAggregateLiteral" else - if this instanceof HC_ClassAggregateLiteral then result = "ClassAggreagateLiteral" else + if this instanceof HC_ClassAggregateLiteral then result = "ClassAggregateLiteral" else if this instanceof HC_DeleteExpr then result = "DeleteExpr" else if this instanceof HC_DeleteArrayExpr then result = "DeleteArrayExpr" else if this instanceof HC_ThrowExpr then result = "ThrowExpr" else @@ -464,7 +464,7 @@ private predicate mk_Deref(HashCons p, PointerDereferenceExpr deref) { private predicate analyzableNonmemberFunctionCall(FunctionCall fc) { forall(int i | - exists(fc.getArgument(i)) | + i in [0..fc.getNumberOfArguments()-1] | strictcount(fc.getArgument(i).getFullyConverted()) = 1 ) and strictcount(fc.getTarget()) = 1 and @@ -487,7 +487,7 @@ private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionC private predicate analyzableExprCall(ExprCall ec) { forall(int i | - exists(ec.getArgument(i)) | + i in [0..ec.getNumberOfArguments()-1] | strictcount(ec.getArgument(i).getFullyConverted()) = 1 ) and strictcount(ec.getExpr().getFullyConverted()) = 1 @@ -508,7 +508,7 @@ private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) { private predicate analyzableMemberFunctionCall( FunctionCall fc) { forall(int i | - exists(fc.getArgument(i)) | + i in [0..fc.getNumberOfArguments()-1] | strictcount(fc.getArgument(i).getFullyConverted()) = 1 ) and strictcount(fc.getTarget()) = 1 and @@ -573,11 +573,15 @@ private predicate mk_ArgConsInner(HashCons head, HC_Args tail, int i, HC_Args li */ private predicate analyzableAllocatorArgZero(ErrorExpr e) { exists(NewOrNewArrayExpr new | - new.getAllocatorCall().getChild(0) = e + new.getAllocatorCall().getChild(0) = e and + strictcount(new.getType().getUnspecifiedType()) = 1 ) + and + strictcount(NewOrNewArrayExpr new | new.getAllocatorCall().getChild(0) = e) = 1 } private predicate mk_AllocatorArgZero(Type t, ErrorExpr e) { + analyzableAllocatorArgZero(e) and exists(NewOrNewArrayExpr new | new.getAllocatorCall().getChild(0) = e and t = new.getType().getUnspecifiedType() @@ -767,7 +771,9 @@ private predicate analyzableClassAggregateLiteral(ClassAggregateLiteral cal) { forall(int i | exists(cal.getChild(i)) | strictcount(cal.getChild(i).getFullyConverted()) = 1 and - strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 + strictcount(Field f | cal.getChild(i) = cal.getFieldExpr(f)) = 1 and + strictcount(Field f, int j | + cal.getFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder()) = 1 ) } @@ -791,10 +797,12 @@ private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) { forall(int i | exists(aal.getChild(i)) | strictcount(aal.getChild(i).getFullyConverted()) = 1 - ) + ) and + strictcount(aal.getType().getUnspecifiedType()) = 1 } private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) { + analyzableArrayAggregateLiteral(aal) and t = aal.getType().getUnspecifiedType() and hc = hashCons(aal.getChild(i)) and ( @@ -935,7 +943,7 @@ cached HashCons hashCons(Expr e) { result = HC_MemberFunctionCall(fcn, qual, args) ) or - // works around an extractor issue class + // works around an extractor issue exists(Type t | mk_AllocatorArgZero(t, e) and result = HC_AllocatorArgZero(t) @@ -972,6 +980,11 @@ cached HashCons hashCons(Expr e) { ) or exists(Type t + | mk_UuidofOperator(t, e) and + result = HC_UuidofOperator(t) + ) + or + exists(Type t | mk_AlignofType(t, e) and result = HC_AlignofType(t) ) From cb9f1269f938eaab6ccaf73a105f4b3123c3d050 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 6 Sep 2018 14:44:02 -0700 Subject: [PATCH 037/110] C++: select example exprs for HashCons portably This makes two changes to how example exprs are selected. Example exprs are now ordered separately by each piece of the location, rather than by stringifying their location. Second, UnknownLocations are now ordered after locations with absolute paths, by using "~" in the lexicographic comparison of absolute paths. I think this works on both POSIX and Windows systems, but it's possible I'm missing a way to start an absolute path with a unicode character. --- .../code/cpp/valuenumbering/HashCons.qll | 14 +++- .../valuenumbering/HashCons/HashCons.expected | 74 +++++++++---------- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll index 7f468aa2da5e..07923d60b034 100644 --- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -277,7 +277,8 @@ class HashCons extends HCBase { result = min(Expr e | this = hashCons(e) - | e order by e.getLocation().toString()) + | e order by exampleLocationString(e.getLocation()), e.getLocation().getStartColumn(), + e.getLocation().getEndLine(), e.getLocation().getEndColumn()) } /** Gets a textual representation of this element. */ @@ -291,6 +292,17 @@ class HashCons extends HCBase { } } +/** + * Gets the absolute path of a known location or "~" for an unknown location. This ensures that + * expressions with unknown locations are ordered after expressions with known locations when + * selecting an example expression for a HashCons value. + */ +private string exampleLocationString(Location l) { + if l instanceof UnknownLocation + then result = "~" + else result = l.getFile().getAbsolutePath() +} + private predicate analyzableIntLiteral(Literal e) { strictcount (e.getValue().toInt()) = 1 and strictcount (e.getType().getUnspecifiedType()) = 1 and diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index 0f92bbd658e9..c050ac2bffd0 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -1,4 +1,3 @@ -| file://:0:0:0:0 | this | 0:c0-c0 141:c23-c26 | | test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 7:c7-c7 | | test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 | | test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 | @@ -24,48 +23,48 @@ | test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 | | test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 | | test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 | -| test.cpp:53:18:53:21 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 | -| test.cpp:53:18:53:21 | (int)... | 53:c18-c21 56:c39-c42 59:c17-c20 | | test.cpp:55:5:55:7 | ptr | 55:c5-c7 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 | -| test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 | -| test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 | +| test.cpp:59:9:59:12 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 | +| test.cpp:59:9:59:12 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 | +| test.cpp:59:17:59:20 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 | +| test.cpp:59:17:59:20 | (int)... | 53:c18-c21 56:c39-c42 59:c17-c20 | | test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 | -| test.cpp:77:20:77:28 | call to getAValue | 77:c20-c28 80:c9-c17 | -| test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 80:c9-c19 | -| test.cpp:79:7:79:7 | v | 79:c7-c7 80:c5-c5 | | test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | -| test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 | +| test.cpp:80:5:80:5 | v | 79:c7-c7 80:c5-c5 | +| test.cpp:80:9:80:17 | call to getAValue | 77:c20-c28 80:c9-c17 | +| test.cpp:80:9:80:19 | (signed short)... | 77:c20-c30 80:c9-c19 | +| test.cpp:92:15:92:16 | 10 | 260:c21-c22 261:c21-c22 92:c15-c16 | +| test.cpp:93:10:93:10 | x | 92:c11-c11 93:c10-c10 | | test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 | | test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 | -| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 239:c11-c11 240:c11-c11 263:c28-c28 264:c28-c28 266:c19-c19 266:c22-c22 285:c5-c5 289:c5-c5 294:c5-c5 299:c9-c9 300:c9-c9 310:c5-c5 311:c5-c5 313:c5-c5 | | test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | -| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 241:c11-c11 263:c24-c24 263:c31-c31 264:c24-c24 264:c31-c31 265:c24-c24 266:c15-c15 267:c15-c15 267:c19-c19 267:c22-c22 269:c15-c15 270:c15-c15 286:c5-c5 290:c5-c5 293:c5-c5 301:c9-c9 302:c9-c9 | | test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 | -| test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 | -| test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 | -| test.cpp:110:15:110:17 | array to pointer conversion | 110:c15-c17 111:c9-c11 | | test.cpp:111:3:111:5 | str | 111:c3-c5 112:c3-c5 113:c3-c5 | +| test.cpp:111:9:111:11 | 1 | 110:c15-c17 111:c9-c11 | +| test.cpp:111:9:111:11 | (char *)... | 110:c15-c17 111:c9-c11 | +| test.cpp:111:9:111:11 | array to pointer conversion | 110:c15-c17 111:c9-c11 | | test.cpp:112:9:112:11 | 2 | 112:c9-c11 113:c9-c11 | | test.cpp:112:9:112:11 | (char *)... | 112:c9-c11 113:c9-c11 | | test.cpp:112:9:112:11 | array to pointer conversion | 112:c9-c11 113:c9-c11 | -| test.cpp:115:13:115:15 | 0.0 | 115:c13-c15 116:c7-c9 | -| test.cpp:115:13:115:15 | (float)... | 115:c13-c15 116:c7-c9 | | test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 | +| test.cpp:116:7:116:9 | 0.0 | 115:c13-c15 116:c7-c9 | +| test.cpp:116:7:116:9 | (float)... | 115:c13-c15 116:c7-c9 | | test.cpp:117:7:117:9 | 0.5 | 117:c7-c9 118:c7-c9 | | test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 | | test.cpp:122:3:122:8 | call to test07 | 122:c3-c8 123:c3-c8 | | test.cpp:125:3:125:11 | call to my_strspn | 125:c3-c11 126:c3-c11 | | test.cpp:125:13:125:17 | array to pointer conversion | 125:c13-c17 126:c13-c17 129:c20-c24 | | test.cpp:125:13:125:17 | foo | 125:c13-c17 126:c13-c17 129:c20-c24 | -| test.cpp:125:20:125:24 | array to pointer conversion | 125:c20-c24 126:c20-c24 129:c13-c17 | -| test.cpp:125:20:125:24 | bar | 125:c20-c24 126:c20-c24 129:c13-c17 | +| test.cpp:129:13:129:17 | array to pointer conversion | 125:c20-c24 126:c20-c24 129:c13-c17 | +| test.cpp:129:13:129:17 | bar | 125:c20-c24 126:c20-c24 129:c13-c17 | | test.cpp:141:12:141:17 | call to getInt | 141:c12-c17 141:c29-c34 | +| test.cpp:141:23:141:26 | this | 0:c0-c0 141:c23-c26 | | test.cpp:146:10:146:11 | ih | 146:c10-c11 146:c31-c32 | | test.cpp:146:13:146:25 | call to getDoubledInt | 146:c13-c25 146:c34-c46 | | test.cpp:150:3:150:3 | x | 150:c3-c3 150:c9-c9 151:c3-c3 151:c9-c9 152:c12-c12 | | test.cpp:150:3:150:5 | ... ++ | 150:c3-c5 150:c9-c11 151:c3-c5 151:c9-c11 152:c10-c12 | | test.cpp:150:3:150:11 | ... + ... | 150:c3-c11 151:c3-c11 | -| test.cpp:156:14:156:20 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | +| test.cpp:156:3:156:9 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | | test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 | | test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 | | test.cpp:179:10:179:22 | (...) | 179:c10-c22 179:c10-c22 | @@ -74,43 +73,44 @@ | test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | | test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 | | test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 245:c25-c35 246:c25-c35 | -| test.cpp:206:25:206:43 | alignof(int_holder) | 206:c25-c43 206:c3-c21 | -| test.cpp:208:27:208:27 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 | +| test.cpp:206:3:206:21 | alignof(int_holder) | 206:c25-c43 206:c3-c21 | +| test.cpp:209:3:209:18 | sizeof() | 209:c22-c37 209:c3-c18 | | test.cpp:209:10:209:15 | holder | 209:c10-c15 209:c29-c34 | | test.cpp:209:10:209:18 | (...) | 209:c10-c18 209:c29-c37 | | test.cpp:209:17:209:17 | x | 209:c17-c17 209:c36-c36 | -| test.cpp:209:22:209:37 | sizeof() | 209:c22-c37 209:c3-c18 | +| test.cpp:210:10:210:10 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 | | test.cpp:210:10:210:11 | (...) | 210:c10-c11 211:c11-c12 211:c24-c25 | -| test.cpp:211:16:211:25 | alignof() | 211:c16-c25 211:c3-c12 | +| test.cpp:211:3:211:12 | alignof() | 211:c16-c25 211:c3-c12 | | test.cpp:239:3:239:12 | new | 239:c3-c12 240:c3-c12 | | test.cpp:245:16:245:36 | new[] | 245:c16-c36 246:c16-c36 | | test.cpp:248:3:248:28 | delete | 248:c3-c28 249:c3-c28 | -| test.cpp:248:10:248:28 | | 248:c10-c28 249:c10-c28 250:c10-c28 252:c10-c32 253:c12-c34 254:c3-c25 255:c3-c25 257:c3-c19 258:c3-c19 260:c3-c23 261:c3-c23 | | test.cpp:248:10:248:28 | call to operator new | 248:c10-c28 249:c10-c28 | | test.cpp:248:10:248:28 | new | 248:c10-c28 249:c10-c28 | -| test.cpp:248:14:248:17 | (void *)... | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | -| test.cpp:248:14:248:17 | ptr1 | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | -| test.cpp:250:14:250:17 | (void *)... | 250:c14-c17 254:c11-c14 | -| test.cpp:250:14:250:17 | ptr2 | 250:c14-c17 254:c11-c14 | | test.cpp:252:10:252:32 | call to operator new | 252:c10-c32 253:c12-c34 | | test.cpp:252:10:252:32 | new | 252:c10-c32 253:c12-c34 | -| test.cpp:252:14:252:15 | 32 | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | -| test.cpp:252:14:252:15 | (size_t)... | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | +| test.cpp:254:3:254:25 | | 248:c10-c28 249:c10-c28 250:c10-c28 252:c10-c32 253:c12-c34 254:c3-c25 255:c3-c25 257:c3-c19 258:c3-c19 260:c3-c23 261:c3-c23 | +| test.cpp:254:7:254:8 | 32 | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | +| test.cpp:254:7:254:8 | (size_t)... | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 | +| test.cpp:254:11:254:14 | (void *)... | 250:c14-c17 254:c11-c14 | +| test.cpp:254:11:254:14 | ptr2 | 250:c14-c17 254:c11-c14 | +| test.cpp:255:11:255:14 | (void *)... | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | +| test.cpp:255:11:255:14 | ptr1 | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 | | test.cpp:257:3:257:19 | call to operator new | 257:c3-c19 258:c3-c19 | | test.cpp:257:3:257:19 | new | 257:c3-c19 258:c3-c19 | | test.cpp:260:3:260:23 | call to operator new[] | 260:c3-c23 261:c3-c23 | | test.cpp:260:3:260:23 | new[] | 260:c3-c23 261:c3-c23 | -| test.cpp:260:21:260:22 | 10 | 260:c21-c22 261:c21-c22 92:c15-c16 | | test.cpp:263:3:263:32 | delete[] | 263:c3-c32 264:c3-c32 | -| test.cpp:263:12:263:32 | | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | -| test.cpp:263:12:263:32 | call to operator new[] | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | | test.cpp:263:12:263:32 | new[] | 263:c12-c32 264:c12-c32 | | test.cpp:263:12:263:32 | {...} | 263:c12-c32 264:c12-c32 | -| test.cpp:265:28:265:28 | 3 | 265:c28-c28 271:c15-c15 35:c16-c16 | +| test.cpp:266:3:266:23 | | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | +| test.cpp:266:3:266:23 | call to operator new[] | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 | | test.cpp:269:3:269:19 | new[] | 269:c3-c19 270:c3-c19 | | test.cpp:269:3:269:19 | {...} | 269:c3-c19 270:c3-c19 | +| test.cpp:271:15:271:15 | 3 | 265:c28-c28 271:c15-c15 35:c16-c16 | | test.cpp:273:3:273:12 | new[] | 273:c3-c12 274:c3-c12 | | test.cpp:273:11:273:11 | x | 273:c11-c11 274:c11-c11 | +| test.cpp:285:5:285:5 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 239:c11-c11 240:c11-c11 263:c28-c28 264:c28-c28 266:c19-c19 266:c22-c22 285:c5-c5 289:c5-c5 294:c5-c5 299:c9-c9 300:c9-c9 310:c5-c5 311:c5-c5 313:c5-c5 | +| test.cpp:286:5:286:5 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 241:c11-c11 263:c24-c24 263:c31-c31 264:c24-c24 264:c31-c31 265:c24-c24 266:c15-c15 267:c15-c15 267:c19-c19 267:c22-c22 269:c15-c15 270:c15-c15 286:c5-c5 290:c5-c5 293:c5-c5 301:c9-c9 302:c9-c9 | | test.cpp:299:3:299:9 | throw ... | 299:c3-c9 300:c3-c9 | | test.cpp:301:3:301:9 | throw ... | 301:c3-c9 302:c3-c9 | | test.cpp:303:3:303:7 | re-throw exception | 303:c3-c7 304:c3-c7 | @@ -124,8 +124,8 @@ | test.cpp:324:3:324:11 | test_19_p | 324:c3-c11 325:c3-c11 326:c3-c11 | | test.cpp:324:3:324:17 | call to expression | 324:c3-c17 325:c3-c17 | | test.cpp:324:13:324:13 | x | 324:c13-c13 325:c13-c13 326:c16-c16 | -| test.cpp:324:16:324:16 | y | 324:c16-c16 325:c16-c16 326:c13-c13 | +| test.cpp:326:13:326:13 | y | 324:c16-c16 325:c16-c16 326:c13-c13 | +| test.cpp:330:3:330:3 | x | 330:c12-c12 330:c3-c3 331:c12-c12 331:c3-c3 332:c12-c12 332:c8-c8 333:c16-c16 333:c3-c3 | | test.cpp:330:3:330:8 | ... == ... | 330:c3-c8 331:c3-c8 333:c3-c8 | | test.cpp:330:3:330:16 | ... ? ... : ... | 330:c3-c16 331:c3-c16 | -| test.cpp:330:12:330:12 | x | 330:c12-c12 330:c3-c3 331:c12-c12 331:c3-c3 332:c12-c12 332:c8-c8 333:c16-c16 333:c3-c3 | -| test.cpp:330:16:330:16 | y | 330:c16-c16 330:c8-c8 331:c16-c16 331:c8-c8 332:c16-c16 332:c3-c3 333:c12-c12 333:c8-c8 | +| test.cpp:332:3:332:3 | y | 330:c16-c16 330:c8-c8 331:c16-c16 331:c8-c8 332:c16-c16 332:c3-c3 333:c12-c12 333:c8-c8 | From 0e44bf3c30432ac0dd91b364db5591014c6c0b22 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 10 Sep 2018 12:22:19 -0700 Subject: [PATCH 038/110] C++: Add import for LGTM --- cpp/ql/src/filters/ImportAdditionalLibraries.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/src/filters/ImportAdditionalLibraries.ql b/cpp/ql/src/filters/ImportAdditionalLibraries.ql index 1d54da5f6e75..261ee3de0ca3 100644 --- a/cpp/ql/src/filters/ImportAdditionalLibraries.ql +++ b/cpp/ql/src/filters/ImportAdditionalLibraries.ql @@ -14,6 +14,7 @@ import semmle.code.cpp.dataflow.DataFlow2 import semmle.code.cpp.dataflow.DataFlow3 import semmle.code.cpp.dataflow.DataFlow4 import semmle.code.cpp.dataflow.TaintTracking +import semmle.code.cpp.valuenumbering.HashCons from File f, string tag where none() From befca6cafacdbd8c8af789a7fb6e86ee55cbd2f9 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Tue, 11 Sep 2018 12:31:00 -0700 Subject: [PATCH 039/110] Remove webview example and its reference in qlhelp file --- .../Electron/EnablingNodeIntegration.qhelp | 19 ++++++------------- .../examples/WebViewNodeIntegration.html | 15 --------------- 2 files changed, 6 insertions(+), 28 deletions(-) delete mode 100644 javascript/ql/src/Electron/examples/WebViewNodeIntegration.html diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp index 47337a03b4e1..c6a9af938d70 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -5,10 +5,10 @@

    - Enabling Node.js integration in web content renderers (BrowserWindow, BrowserView and webview) could result in + Enabling Node.js integration in web content renderers (BrowserWindow, BrowserView and webview) could result in remote native code execution attacks when rendering malicious JavaScript code from untrusted remote web site or - code that is injected via a cross site scripting vulnerability into the web content under processing. Please note that - the nodeIntegration property is enabled by default in Electron and needs to be set to 'false' explicitly. + code that is injected via a cross site scripting vulnerability into a trusted remote web site. Note that + the nodeIntegration property is enabled by default in Electron and needs to be set to false explicitly.

    @@ -21,28 +21,21 @@

    - The following example shows insecure use of BrowserWindow with regards to nodeIntegration + The following example shows insecure use of BrowserWindow with regards to nodeIntegration property:

    - This is problematic, because default value of nodeIntegration is 'true'. + This is problematic, because default value of nodeIntegration is 'true'.

    - -

    - The following example shows insecure and secure uses of webview tag: -

    - - -

    - The following example shows insecure and secure uses of BrowserWindow and BrowserView when + The following example shows insecure and secure uses of BrowserWindow and BrowserView when loading untrusted web sites:

    diff --git a/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html b/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html deleted file mode 100644 index 1deaa6de3326..000000000000 --- a/javascript/ql/src/Electron/examples/WebViewNodeIntegration.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - WebView Examples - - - - - - - - - - \ No newline at end of file From fc087ffb71c6ef5bd3a264db845b0fdab68db410 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Tue, 11 Sep 2018 12:32:56 -0700 Subject: [PATCH 040/110] Replaceing query and test files with suggested ones --- .../src/Electron/EnablingNodeIntegration.ql | 36 ++++++++++--------- .../EnablingNodeIntegration.expected | 10 +++--- .../EnablingNodeIntegration.js | 17 ++++----- .../EnablingNodeIntegration.qlref | 2 +- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.ql b/javascript/ql/src/Electron/EnablingNodeIntegration.ql index 39b19c5d43b7..f38d854c1dc9 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.ql +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.ql @@ -1,28 +1,32 @@ /** - * @name Enabling nodeIntegration and nodeIntegrationInWorker in webPreferences - * @description Enabling nodeIntegration and nodeIntegrationInWorker could expose your app to remote code execution. + * @name Enabling `nodeIntegration` or `nodeIntegrationInWorker` for Electron web content + * @description Enabling `nodeIntegration` or `nodeIntegrationInWorker` can expose the application to remote code execution. * @kind problem * @problem.severity warning - * @precision very-high + * @id js/enabling-electron-renderer-node-integration * @tags security * frameworks/electron - * @id js/enabling-electron-renderer-node-integration */ import javascript -string checkWebOptions(DataFlow::PropWrite prop, Electron::WebPreferences pref) { - (prop = pref.getAPropertyWrite("nodeIntegration") and - prop.getRhs().mayHaveBooleanValue(true) and - result = "nodeIntegration property may have been enabled on this object that could result in RCE") - or - (prop = pref.getAPropertyWrite("nodeIntegrationInWorker") and - prop.getRhs().mayHaveBooleanValue(true) and - result = "nodeIntegrationInWorker property may have been enabled on this object that could result in RCE") +/** + * Gets a warning message for `pref` if one of the `nodeIntegration` features is enabled. + */ +string getNodeIntegrationWarning(Electron::WebPreferences pref) { + exists (string feature | + feature = "nodeIntegration" or + feature = "nodeIntegrationInWorker" | + pref.getAPropertyWrite(feature).getRhs().mayHaveBooleanValue(true) and + result = "The `" + feature + "` feature has been enabled." + ) or - (not exists(pref.asExpr().(ObjectExpr).getPropertyByName("nodeIntegration")) and - result = "nodeIntegration is enabled by default in WebPreferences object that could result in RCE") + exists (string feature | + feature = "nodeIntegration" | + not exists(pref.getAPropertyWrite(feature)) and + result = "The `" + feature + "` feature is enabled by default." + ) } -from DataFlow::PropWrite property, Electron::WebPreferences preferences -select preferences,checkWebOptions(property, preferences) \ No newline at end of file +from Electron::WebPreferences preferences +select preferences, getNodeIntegrationWarning(preferences) \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected index 092ad577de35..674fd74caee2 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected @@ -1,5 +1,5 @@ -| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegration property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | nodeIntegrationInWorker property may have been enabled on this object that could result in RCE | -| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | -| EnablingNodeIntegration.js:23:13:27:9 | {\\n ... } | nodeIntegration is enabled by default in WebPreferences object that could result in RCE | -| EnablingNodeIntegration.js:49:71:49:93 | {nodeIn ... : true} | nodeIntegration property may have been enabled on this object that could result in RCE | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | The `nodeIntegrationInWorker` feature has been enabled. | +| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | The `nodeIntegration` feature has been enabled. | +| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | The `nodeIntegration` feature is enabled by default. | +| EnablingNodeIntegration.js:23:16:27:9 | { // NO ... } | The `nodeIntegration` feature is enabled by default. | +| EnablingNodeIntegration.js:49:74:49:96 | {nodeIn ... : true} | The `nodeIntegration` feature has been enabled. | diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js index fd1a4201df7a..5e1d0e95fb43 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js @@ -1,7 +1,7 @@ const {BrowserWindow} = require('electron') function test() { - var unsafe_1 = { + var unsafe_1 = { // NOT OK, both enabled webPreferences: { nodeIntegration: true, nodeIntegrationInWorker: true, @@ -11,7 +11,7 @@ function test() { } }; - var options_1 = { + var options_1 = { // NOT OK, `nodeIntegrationInWorker` enabled webPreferences: { plugins: true, nodeIntegrationInWorker: false, @@ -20,13 +20,13 @@ function test() { } }; - var pref = { + var pref = { // NOT OK, implicitly enabled plugins: true, webSecurity: true, sandbox: true }; - var options_2 = { + var options_2 = { // NOT OK, implicitly enabled webPreferences: pref, show: true, frame: true, @@ -34,7 +34,7 @@ function test() { minHeight: 300 }; - var safe_used = { + var safe_used = { // NOT OK, explicitly disabled webPreferences: { nodeIntegration: false, plugins: true, @@ -46,6 +46,7 @@ function test() { var w1 = new BrowserWindow(unsafe_1); var w2 = new BrowserWindow(options_1); var w3 = new BrowserWindow(safe_used); - var w4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); - var w5 = new BrowserWindow(options_2); -} \ No newline at end of file + var w4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); // NOT OK, `nodeIntegration` enabled + var w5 = new BrowserWindow(options_2); + var w6 = new BrowserWindow(safe_used); +} diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref index 3f8dbad0d571..b0315fd89ad5 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref @@ -1 +1 @@ -../../../../src/Electron/EnablingNodeIntegration.ql \ No newline at end of file +Electron/EnablingNodeIntegration.ql From ecd08d4560a05a151044dcf858cfcf101c839911 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Wed, 12 Sep 2018 12:05:57 -0700 Subject: [PATCH 041/110] Chaning EOL in two files --- .../Electron/NodeIntegration/EnablingNodeIntegration.qlref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref index b0315fd89ad5..3f8dbad0d571 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref @@ -1 +1 @@ -Electron/EnablingNodeIntegration.ql +../../../../src/Electron/EnablingNodeIntegration.ql \ No newline at end of file From 1220b50737119b08356ecccdf91ed50ac356cd59 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 21:37:02 +0200 Subject: [PATCH 042/110] JS: whitelist _.bindAll-methods in js/unbound-event-handler-receiver --- .../UnboundEventHandlerReceiver.ql | 20 +++++++-- .../UnboundEventHandlerReceiver.expected | 6 +-- .../UnboundEventHandlerReceiver/tst.js | 45 ++++++++++++------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql index 63212b69bf18..8875a376c16f 100644 --- a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql +++ b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql @@ -13,16 +13,30 @@ import javascript * Holds if the receiver of `method` is bound in a method of its class. */ private predicate isBoundInMethod(MethodDeclaration method) { - exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod | + exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod, string name | + name = method.getName() and bindingMethod.getDeclaringClass() = method.getDeclaringClass() and not bindingMethod.isStatic() and - thiz.getBinder().getAstNode() = bindingMethod.getBody() and + thiz.getBinder().getAstNode() = bindingMethod.getBody() | exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind | // this. = .bind(...) - thiz.hasPropertyWrite(method.getName(), rhs) and + thiz.hasPropertyWrite(name, rhs) and bind.flowsTo(rhs) and bind.getMethodName() = "bind" ) + or + exists (DataFlow::MethodCallNode bindAll | + bindAll.getMethodName() = "bindAll" and + thiz.flowsTo(bindAll.getArgument(0)) | + // _.bindAll(this, ) + bindAll.getArgument(1).mayHaveStringValue(name) + or + // _.bindAll(this, [, ]) + exists (DataFlow::ArrayLiteralNode names | + names.flowsTo(bindAll.getArgument(1)) and + names.getAnElement().mayHaveStringValue(name) + ) + ) ) } diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected index d0cba5116dc7..d0c16fa6c0e9 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected @@ -1,3 +1,3 @@ -| tst.js:56:18:56:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:14:9:14:12 | this | this | tst.js:13:5:15:5 | unbound ... ;\\n } | unbound1 | -| tst.js:57:18:57:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:18:15:18:18 | this | this | tst.js:17:5:19:5 | unbound ... ;\\n } | unbound2 | -| tst.js:58:18:58:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:22:15:22:18 | this | this | tst.js:21:5:23:5 | unbound ... ;\\n } | unbound3 | +| tst.js:8:18:8:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:35:9:35:12 | this | this | tst.js:34:5:36:5 | unbound ... ;\\n } | unbound1 | +| tst.js:9:18:9:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:39:15:39:18 | this | this | tst.js:38:5:40:5 | unbound ... ;\\n } | unbound2 | +| tst.js:10:18:10:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:43:15:43:18 | this | this | tst.js:42:5:44:5 | unbound ... ;\\n } | unbound3 | diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js index 196fcea7820d..0d9334a9fd8d 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js @@ -1,6 +1,25 @@ import React from 'react'; class Component extends React.Component { + + render() { + var unbound3 = this.unbound3; + return
    +
    // NOT OK +
    // NOT OK +
    // NOT OK +
    // OK +
    // OK +
    // OK +
    // OK +
    this.unbound_butInvokedSafely(e)}/> // OK +
    // OK +
    // OK +
    // OK +
    // OK +
    + } + constructor(props) { super(props); this.bound_throughBindInConstructor = this.bound_throughBindInConstructor.bind(this); @@ -8,6 +27,8 @@ class Component extends React.Component { var cmp = this; var bound = (cmp.bound_throughNonSyntacticBindInConstructor.bind(this)); (cmp).bound_throughNonSyntacticBindInConstructor = bound; + _.bindAll(this, 'bound_throughBindAllInConstructor1'); + _.bindAll(this, ['bound_throughBindAllInConstructor2']); } unbound1() { @@ -50,22 +71,6 @@ class Component extends React.Component { this.setState({ }); } - render() { - var unbound3 = this.unbound3; - return
    -
    // NOT OK -
    // NOT OK -
    // NOT OK -
    // OK -
    // OK -
    // OK -
    // OK -
    this.unbound_butInvokedSafely(e)}/> // OK -
    // OK -
    // OK -
    - } - componentWillMount() { this.bound_throughBindInMethod = this.bound_throughBindInMethod.bind(this); } @@ -74,6 +79,14 @@ class Component extends React.Component { this.setState({ }); } + bound_throughBindAllInConstructor1() { + this.setState({ }); + } + + bound_throughBindAllInConstructor2() { + this.setState({ }); + } + } // semmle-extractor-options: --experimental From eb10f603ab16c6e1c319ba91be25d277650d6dd2 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 21:51:51 +0200 Subject: [PATCH 043/110] JS: whitelist decorator-bound methods in js/unbound-event-handler-receiver --- .../src/Expressions/UnboundEventHandlerReceiver.ql | 11 ++++++++++- .../UnboundEventHandlerReceiver.expected | 6 +++--- .../Expressions/UnboundEventHandlerReceiver/tst.js | 12 ++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql index 8875a376c16f..1c99ba22caa4 100644 --- a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql +++ b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql @@ -10,7 +10,7 @@ import javascript /** - * Holds if the receiver of `method` is bound in a method of its class. + * Holds if the receiver of `method` is bound. */ private predicate isBoundInMethod(MethodDeclaration method) { exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod, string name | @@ -38,6 +38,15 @@ private predicate isBoundInMethod(MethodDeclaration method) { ) ) ) + or + exists (Expr decoration, string name | + decoration = method.getADecorator().getExpression() and + name.regexpMatch("(?i).*(bind|bound).*") | + // @autobind + decoration.(Identifier).getName() = name or + // @action.bound + decoration.(PropAccess).getPropertyName() = name + ) } /** diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected index d0c16fa6c0e9..854a77745bfa 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected @@ -1,3 +1,3 @@ -| tst.js:8:18:8:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:35:9:35:12 | this | this | tst.js:34:5:36:5 | unbound ... ;\\n } | unbound1 | -| tst.js:9:18:9:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:39:15:39:18 | this | this | tst.js:38:5:40:5 | unbound ... ;\\n } | unbound2 | -| tst.js:10:18:10:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:43:15:43:18 | this | this | tst.js:42:5:44:5 | unbound ... ;\\n } | unbound3 | +| tst.js:8:18:8:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:37:9:37:12 | this | this | tst.js:36:5:38:5 | unbound ... ;\\n } | unbound1 | +| tst.js:9:18:9:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:41:15:41:18 | this | this | tst.js:40:5:42:5 | unbound ... ;\\n } | unbound2 | +| tst.js:10:18:10:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:45:15:45:18 | this | this | tst.js:44:5:46:5 | unbound ... ;\\n } | unbound3 | diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js index 0d9334a9fd8d..838b39c58b4e 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js @@ -17,6 +17,8 @@ class Component extends React.Component {
    // OK
    // OK
    // OK +
    // OK +
    // OK
    } @@ -87,6 +89,16 @@ class Component extends React.Component { this.setState({ }); } + @autobind + bound_throughDecorator_autobind() { + this.setState({ }); + } + + @action.bound + bound_throughDecorator_actionBound() { + this.setState({ }); + } + } // semmle-extractor-options: --experimental From fcc33ce93dcf2d13c25351db26e34b3c2a044d5a Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 22:21:02 +0200 Subject: [PATCH 044/110] JS: whitelist auto-bind methods in js/unbound-event-handler-receiver --- .../UnboundEventHandlerReceiver.ql | 39 +++++++++++-------- .../UnboundEventHandlerReceiver.expected | 6 +-- .../UnboundEventHandlerReceiver/tst.js | 21 +++++++++- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql index 1c99ba22caa4..de947b250ab6 100644 --- a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql +++ b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql @@ -13,28 +13,33 @@ import javascript * Holds if the receiver of `method` is bound. */ private predicate isBoundInMethod(MethodDeclaration method) { - exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod, string name | - name = method.getName() and + exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod | bindingMethod.getDeclaringClass() = method.getDeclaringClass() and not bindingMethod.isStatic() and thiz.getBinder().getAstNode() = bindingMethod.getBody() | - exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind | - // this. = .bind(...) - thiz.hasPropertyWrite(name, rhs) and - bind.flowsTo(rhs) and - bind.getMethodName() = "bind" - ) + // require("auto-bind")(this) + thiz.flowsTo(DataFlow::moduleImport("auto-bind").getACall().getArgument(0)) or - exists (DataFlow::MethodCallNode bindAll | - bindAll.getMethodName() = "bindAll" and - thiz.flowsTo(bindAll.getArgument(0)) | - // _.bindAll(this, ) - bindAll.getArgument(1).mayHaveStringValue(name) + exists (string name | + name = method.getName() | + exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind | + // this. = .bind(...) + thiz.hasPropertyWrite(name, rhs) and + bind.flowsTo(rhs) and + bind.getMethodName() = "bind" + ) or - // _.bindAll(this, [, ]) - exists (DataFlow::ArrayLiteralNode names | - names.flowsTo(bindAll.getArgument(1)) and - names.getAnElement().mayHaveStringValue(name) + exists (DataFlow::MethodCallNode bindAll | + bindAll.getMethodName() = "bindAll" and + thiz.flowsTo(bindAll.getArgument(0)) | + // _.bindAll(this, ) + bindAll.getArgument(1).mayHaveStringValue(name) + or + // _.bindAll(this, [, ]) + exists (DataFlow::ArrayLiteralNode names | + names.flowsTo(bindAll.getArgument(1)) and + names.getAnElement().mayHaveStringValue(name) + ) ) ) ) diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected index 854a77745bfa..9d3ad4447d7a 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected @@ -1,3 +1,3 @@ -| tst.js:8:18:8:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:37:9:37:12 | this | this | tst.js:36:5:38:5 | unbound ... ;\\n } | unbound1 | -| tst.js:9:18:9:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:41:15:41:18 | this | this | tst.js:40:5:42:5 | unbound ... ;\\n } | unbound2 | -| tst.js:10:18:10:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:45:15:45:18 | this | this | tst.js:44:5:46:5 | unbound ... ;\\n } | unbound3 | +| tst.js:27:18:27:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:56:9:56:12 | this | this | tst.js:55:5:57:5 | unbound ... ;\\n } | unbound1 | +| tst.js:28:18:28:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:60:15:60:18 | this | this | tst.js:59:5:61:5 | unbound ... ;\\n } | unbound2 | +| tst.js:29:18:29:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:64:15:64:18 | this | this | tst.js:63:5:65:5 | unbound ... ;\\n } | unbound3 | diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js index 838b39c58b4e..1b5265722258 100644 --- a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js +++ b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/tst.js @@ -1,6 +1,25 @@ import React from 'react'; +import autoBind from 'auto-bind'; -class Component extends React.Component { +class Component0 extends React.Component { + + render() { + return
    +
    // OK +
    + } + + constructor(props) { + super(props); + autoBind(this); + } + + bound_throughAutoBind() { + this.setState({ }); + } +} + +class Component1 extends React.Component { render() { var unbound3 = this.unbound3; From 52013f3071c3fb678cf24f467f3d1bd3b2229a21 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 13 Sep 2018 08:43:01 +0200 Subject: [PATCH 045/110] JS: change notes for improved js/unbound-event-handler-receiver --- change-notes/1.19/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 5fdf442d1289..86c784b1cae1 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -13,6 +13,7 @@ | **Query** | **Expected impact** | **Change** | |--------------------------------|----------------------------|----------------------------------------------| | Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. | +| Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. | ## Changes to QL libraries From ea37665ec69a93b83995349a49763b7a96955e6d Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 14:27:08 +0200 Subject: [PATCH 046/110] JS: move array-specific taint steps to separate class --- .../javascript/dataflow/TaintTracking.qll | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 671729b9b2f3..03b164172842 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -185,18 +185,44 @@ module TaintTracking { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { succ = this and - ( - exists (Expr e, Expr f | e = this.asExpr() and f = pred.asExpr() | - // arrays with tainted elements and objects with tainted property names are tainted - e.(ArrayExpr).getAnElement() = f or - exists (Property prop | e.(ObjectExpr).getAProperty() = prop | - prop.isComputed() and f = prop.getNameExpr() - ) - or - // awaiting a tainted expression gives a tainted result - e.(AwaitExpr).getOperand() = f + exists (Expr e, Expr f | e = this.asExpr() and f = pred.asExpr() | + // arrays with tainted elements and objects with tainted property names are tainted + e.(ArrayExpr).getAnElement() = f or + exists (Property prop | e.(ObjectExpr).getAProperty() = prop | + prop.isComputed() and f = prop.getNameExpr() ) or + // awaiting a tainted expression gives a tainted result + e.(AwaitExpr).getOperand() = f + ) + or + // reading from a tainted object yields a tainted result + this = succ and + succ.(DataFlow::PropRead).getBase() = pred + or + // iterating over a tainted iterator taints the loop variable + exists (EnhancedForLoop efl, SsaExplicitDefinition ssa | + this = DataFlow::valueNode(efl.getIterationDomain()) and + pred = this and + ssa.getDef() = efl.getIteratorExpr() and + succ = DataFlow::ssaDefinitionNode(ssa) + ) + } + } + + /** + * A taint propagating data flow edge caused by the builtin array functions. + */ + private class ArrayFunctionTaintStep extends AdditionalTaintStep { + + ArrayFunctionTaintStep() { + this = DataFlow::valueNode(_) or + this = DataFlow::parameterNode(_) or + this instanceof DataFlow::PropRead + } + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + succ = this and ( // `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are // `elt` and `ary`; similar for `forEach` exists (MethodCallExpr m, Function f, int i, SimpleParameter p | @@ -218,19 +244,8 @@ module TaintTracking { // `array.push(e)`: if `e` is tainted, then so is `array` succ.(DataFlow::SourceNode).getAMethodCall("push").getAnArgument() = pred ) - or - // reading from a tainted object yields a tainted result - this = succ and - succ.(DataFlow::PropRead).getBase() = pred - or - // iterating over a tainted iterator taints the loop variable - exists (EnhancedForLoop efl, SsaExplicitDefinition ssa | - this = DataFlow::valueNode(efl.getIterationDomain()) and - pred = this and - ssa.getDef() = efl.getIteratorExpr() and - succ = DataFlow::ssaDefinitionNode(ssa) - ) } + } /** From 763da72ce5275890d0be82fd9adcbe47b2ef53b3 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 15:09:35 +0200 Subject: [PATCH 047/110] JS: modernize old array taint steps --- .../javascript/dataflow/TaintTracking.qll | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 03b164172842..2ae0a5ae4330 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -214,36 +214,33 @@ module TaintTracking { * A taint propagating data flow edge caused by the builtin array functions. */ private class ArrayFunctionTaintStep extends AdditionalTaintStep { + DataFlow::CallNode call; ArrayFunctionTaintStep() { - this = DataFlow::valueNode(_) or - this = DataFlow::parameterNode(_) or - this instanceof DataFlow::PropRead + this = call } override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - succ = this and ( - // `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are - // `elt` and `ary`; similar for `forEach` - exists (MethodCallExpr m, Function f, int i, SimpleParameter p | - (m.getMethodName() = "map" or m.getMethodName() = "forEach") and - (i = 0 or i = 2) and - m.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and - p = f.getParameter(i) and - this = DataFlow::parameterNode(p) and - pred.asExpr() = m.getReceiver() - ) - or - // `array.map` with tainted return value in callback - exists (MethodCallExpr m, Function f | - this.asExpr() = m and - m.getMethodName() = "map" and - m.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow - pred = f.getAReturnedExpr().flow()) - or - // `array.push(e)`: if `e` is tainted, then so is `array` - succ.(DataFlow::SourceNode).getAMethodCall("push").getAnArgument() = pred + // `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are + // `elt` and `ary`; similar for `forEach` + exists (string name, Function f, int i | + (name = "map" or name = "forEach") and + (i = 0 or i = 2) and + call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and + pred.(DataFlow::SourceNode).getAMethodCall(name) = call and + succ = DataFlow::parameterNode(f.getParameter(i)) ) + or + // `array.map` with tainted return value in callback + exists (DataFlow::FunctionNode f | + call.(DataFlow::MethodCallNode).getMethodName() = "map" and + call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow + pred = f.getAReturn() and + succ = call + ) + or + // `array.push(e)`: if `e` is tainted, then so is `array` + succ.(DataFlow::SourceNode).getAMethodCall("push") = call } } From 1a14b13703e9bf6d12b6b989707ed75e98db9296 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 13 Sep 2018 09:53:41 -0700 Subject: [PATCH 048/110] C++: migrate change note --- change-notes/1.18/analysis-cpp.md | 3 +-- change-notes/1.19/analysis-cpp.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 change-notes/1.19/analysis-cpp.md diff --git a/change-notes/1.18/analysis-cpp.md b/change-notes/1.18/analysis-cpp.md index 7f3f28453874..00877a124623 100644 --- a/change-notes/1.18/analysis-cpp.md +++ b/change-notes/1.18/analysis-cpp.md @@ -31,11 +31,10 @@ | [Uncontrolled data in arithmetic expression] | More correct results | Increment / decrement / addition assignment / subtraction assignment operations are now understood as arithmetic operations in this query. | | [Use of extreme values in arithmetic expression] | More correct results | Increment / decrement / addition assignment / subtraction assignment operations are now understood as arithmetic operations in this query. | | [Use of extreme values in arithmetic expression] | Fewer false positives | The query now considers whether a particular expression might cause an overflow of minimum or maximum values only. | - + ## Changes to QL libraries * Fixes for aggregate initializers using designators: * `ClassAggregateLiteral.getFieldExpr()` previously assumed initializer expressions appeared in the same order as the declaration order of the fields, causing it to associate the expressions with the wrong fields when using designated initializers. This has been fixed. * `ArrayAggregateLiteral.getElementExpr()` previously assumed initializer expressions appeared in the same order as the corresponding array elements, causing it to associate the expressions with the wrong array elements when using designated initializers. This has been fixed. * `Element.getEnclosingElement()` no longer includes macro accesses in its results. To explore parents and children of macro accesses, use the relevant member predicates on `MacroAccess` or `MacroInvocation`. -* Added a hash consing library for structural comparison of expressions. diff --git a/change-notes/1.19/analysis-cpp.md b/change-notes/1.19/analysis-cpp.md new file mode 100644 index 000000000000..17a165e5928a --- /dev/null +++ b/change-notes/1.19/analysis-cpp.md @@ -0,0 +1,20 @@ +# Improvements to C/C++ analysis + +## General improvements + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* | + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* | + + +## Changes to QL libraries + +* Added a hash consing library for structural comparison of expressions. From 4c13e6b46b182d733bba356479feeab5dbeec8bf Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 11 Sep 2018 15:10:59 +0200 Subject: [PATCH 049/110] JS: add additional array-specific taint steps --- .../javascript/dataflow/TaintTracking.qll | 24 +++++++++++++++-- .../TaintTracking/BasicTaintTracking.expected | 12 +++++++++ .../test/library-tests/TaintTracking/tst.js | 26 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 2ae0a5ae4330..55f8ec8b9e7e 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -239,8 +239,28 @@ module TaintTracking { succ = call ) or - // `array.push(e)`: if `e` is tainted, then so is `array` - succ.(DataFlow::SourceNode).getAMethodCall("push") = call + // `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`. + exists (string name | + name = "push" or + name = "unshift" | + pred = call.getAnArgument() and + succ.(DataFlow::SourceNode).getAMethodCall(name) = call + ) + or + // `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`. + exists (string name | + name = "pop" or + name = "shift" or + name = "slice" or + name = "splice" | + call.(DataFlow::MethodCallNode).calls(pred, name) and + succ = call + ) + or + // `e = Array.from(x)`: if `x` is tainted, then so is `e`. + call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and + pred = call.getAnArgument() and + succ = call } } diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index a30f0b1bf2bb..6dcfbe315bfd 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -3,3 +3,15 @@ | tst.js:2:13:2:20 | source() | tst.js:14:10:14:17 | x.sort() | | tst.js:2:13:2:20 | source() | tst.js:17:10:17:10 | a | | tst.js:2:13:2:20 | source() | tst.js:19:10:19:10 | a | +| tst.js:2:13:2:20 | source() | tst.js:23:10:23:10 | b | +| tst.js:2:13:2:20 | source() | tst.js:25:10:25:16 | x.pop() | +| tst.js:2:13:2:20 | source() | tst.js:26:10:26:18 | x.shift() | +| tst.js:2:13:2:20 | source() | tst.js:27:10:27:18 | x.slice() | +| tst.js:2:13:2:20 | source() | tst.js:28:10:28:19 | x.splice() | +| tst.js:2:13:2:20 | source() | tst.js:30:10:30:22 | Array.from(x) | +| tst.js:2:13:2:20 | source() | tst.js:33:14:33:16 | elt | +| tst.js:2:13:2:20 | source() | tst.js:35:14:35:16 | ary | +| tst.js:2:13:2:20 | source() | tst.js:39:14:39:16 | elt | +| tst.js:2:13:2:20 | source() | tst.js:41:14:41:16 | ary | +| tst.js:2:13:2:20 | source() | tst.js:44:10:44:30 | innocen ... ) => x) | +| tst.js:2:13:2:20 | source() | tst.js:45:10:45:24 | x.map(x2 => x2) | diff --git a/javascript/ql/test/library-tests/TaintTracking/tst.js b/javascript/ql/test/library-tests/TaintTracking/tst.js index 69b5f17074ce..48c7ace4611a 100644 --- a/javascript/ql/test/library-tests/TaintTracking/tst.js +++ b/javascript/ql/test/library-tests/TaintTracking/tst.js @@ -18,4 +18,30 @@ function test() { a.push(x); sink(a); // NOT OK + var b = []; + b.unshift(x); + sink(b); // NOT OK + + sink(x.pop()); // NOT OK + sink(x.shift()); // NOT OK + sink(x.slice()); // NOT OK + sink(x.splice()); // NOT OK + + sink(Array.from(x)); // NOT OK + + x.map((elt, i, ary) => { + sink(elt); // NOT OK + sink(i); // OK + sink(ary); // NOT OK + }); + + x.forEach((elt, i, ary) => { + sink(elt); // NOT OK + sink(i); // OK + sink(ary); // NOT OK + }); + + sink(innocent.map(() => x)); // NOT OK + sink(x.map(x2 => x2)); // NOT OK + } From cb2bd9e0aef050188b38e596a5af5da94fe187b9 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 13 Sep 2018 15:52:40 +0200 Subject: [PATCH 050/110] JS: change notes for additional array taint steps --- change-notes/1.19/analysis-javascript.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 5fdf442d1289..0e9b224e26dc 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -2,6 +2,8 @@ ## General improvements +* Modelling of taint flow through array operations has been improved. This may give additional results for the security queries. + ## New queries | **Query** | **Tags** | **Purpose** | From 28050e1415de70c25b050237ab7e9900f81b0824 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Thu, 13 Sep 2018 15:44:32 -0700 Subject: [PATCH 051/110] Change to cpp/overflow-buffer to detect access to an array using a negative index (static, out of range access, lower bound). --- .gitignore | 4 ++++ cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 31f8ccd9abf2..4b055e55a091 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ # qltest projects and artifacts */ql/test/**/*.testproj */ql/test/**/*.actual +/.vs/slnx.sqlite +/.vs/ql/v15/Browse.VC.opendb +/.vs/ql/v15/Browse.VC.db +/.vs/ProjectSettings.json diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql index c15552384e98..ec1d84b8d29c 100644 --- a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql +++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql @@ -29,7 +29,7 @@ from BufferAccess ba, string bufferDesc, int accessSize, int accessType, where accessSize = ba.getSize() and bufferSize = getBufferSize(ba.getBuffer(bufferDesc, accessType), bufferAlloc) - and accessSize > bufferSize + and (accessSize > bufferSize or (accessSize <= 0 and accessType = 3)) and if accessType = 1 then ( message = "This '" + ba.getName() + "' operation accesses " + plural(accessSize, " byte", " bytes") @@ -41,8 +41,13 @@ where accessSize = ba.getSize() + " but the $@ is only " + plural(bufferSize, " byte", " bytes") + "." ) else ( - message = "This array indexing operation accesses byte offset " - + (accessSize - 1) + " but the $@ is only " - + plural(bufferSize, " byte", " bytes") + "." + if accessSize > 0 then ( + message = "This array indexing operation accesses byte offset " + + (accessSize - 1) + " but the $@ is only " + + plural(bufferSize, " byte", " bytes") + "." + ) else ( + message = "This array indexing operation accesses a negative index " + + ((accessSize/ba.getActualType().getSize()) - 1) + " on the $@." + ) ) select ba, message, bufferAlloc, bufferDesc From 81aeda69e1270dbb48ee13c637b46c6670f6f73a Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 13 Sep 2018 21:46:46 +0200 Subject: [PATCH 052/110] JS: lower @precision of js/remote-property-injection --- change-notes/1.19/analysis-javascript.md | 2 +- javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 86c784b1cae1..b94b015d3720 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -14,6 +14,6 @@ |--------------------------------|----------------------------|----------------------------------------------| | Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. | | Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. | - +| Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. | ## Changes to QL libraries diff --git a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql index e3a9dcd74ba2..ae70f83ed87b 100644 --- a/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql +++ b/javascript/ql/src/Security/CWE-400/RemotePropertyInjection.ql @@ -5,7 +5,7 @@ * * @kind problem * @problem.severity warning - * @precision high + * @precision medium * @id js/remote-property-injection * @tags security * external/cwe/cwe-250 From 8de269e1fb9abbab7729406718104e432b3a444e Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 30 Aug 2018 08:51:57 +0200 Subject: [PATCH 053/110] JS: add support for `fs-extra` in `NodeJSFileSystemAccess` --- javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index ba4dc9731e7f..1d911f7a2e2c 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -338,14 +338,14 @@ module NodeJSLib { /** - * A call to a method from module `fs` or `graceful-fs`. + * A call to a method from module `fs`, `graceful-fs` or `fs-extra`. */ private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode { string methodName; NodeJSFileSystemAccess() { exists (string moduleName | this = DataFlow::moduleMember(moduleName, methodName).getACall() | - moduleName = "fs" or moduleName = "graceful-fs" + moduleName = "fs" or moduleName = "graceful-fs" or moduleName = "fs-extra" ) } From 6d3c1a1d225a9cb01e1a9af27ad3400bd7a1e9dd Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 30 Aug 2018 08:59:22 +0200 Subject: [PATCH 054/110] JS: introduce `fsModuleMember` --- .../semmle/javascript/frameworks/NodeJSLib.qll | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index 1d911f7a2e2c..b277663b3ddf 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -336,6 +336,17 @@ module NodeJSLib { ) } + /** + * A member `member` from module `fs` or its drop-in replacements `graceful-fs` or `fs-extra`. + */ + private DataFlow::SourceNode fsModuleMember(string member) { + exists (string moduleName | + moduleName = "fs" or + moduleName = "graceful-fs" or + moduleName = "fs-extra" | + result = DataFlow::moduleMember(moduleName, member) + ) + } /** * A call to a method from module `fs`, `graceful-fs` or `fs-extra`. @@ -344,9 +355,7 @@ module NodeJSLib { string methodName; NodeJSFileSystemAccess() { - exists (string moduleName | this = DataFlow::moduleMember(moduleName, methodName).getACall() | - moduleName = "fs" or moduleName = "graceful-fs" or moduleName = "fs-extra" - ) + this = fsModuleMember(methodName).getACall() } override DataFlow::Node getAPathArgument() { From e2fac8a03cae6cd04eae640ce8419e8f5dc08124 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 30 Aug 2018 12:52:39 +0200 Subject: [PATCH 055/110] JS: introduce concept: `FileNameSource` --- javascript/ql/src/semmle/javascript/Concepts.qll | 7 +++++++ .../semmle/javascript/frameworks/NodeJSLib.qll | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/Concepts.qll b/javascript/ql/src/semmle/javascript/Concepts.qll index 1ab9f8cba2ed..ce470f882b74 100644 --- a/javascript/ql/src/semmle/javascript/Concepts.qll +++ b/javascript/ql/src/semmle/javascript/Concepts.qll @@ -29,6 +29,13 @@ abstract class FileSystemAccess extends DataFlow::Node { abstract DataFlow::Node getAPathArgument(); } +/** + * A data flow node that contains a file name or an array of file names from the local file system. + */ +abstract class FileNameSource extends DataFlow::Node { + +} + /** * A data flow node that performs a database access. */ diff --git a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index b277663b3ddf..4328d8fdee44 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -365,6 +365,22 @@ module NodeJSLib { } } + /** + * A data flow node that contains a file name or an array of file names from the local file system. + */ + private class NodeJSFileNameSource extends FileNameSource { + + NodeJSFileNameSource() { + exists (string name | + name = "readdir" or + name = "realpath" | + this = fsModuleMember(name).getACall().getCallback([1..2]).getParameter(1) or + this = fsModuleMember(name + "Sync").getACall() + ) + } + + } + /** * A call to a method from module `child_process`. */ From 33f98dd1a7c6381a915faa248550d50c024731ee Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 30 Aug 2018 12:52:59 +0200 Subject: [PATCH 056/110] JS: add query: js/stored-xss --- javascript/config/suites/javascript/security | 1 + .../ql/src/Security/CWE-079/StoredXss.qhelp | 63 +++++++++++++++++++ .../ql/src/Security/CWE-079/StoredXss.ql | 20 ++++++ .../Security/CWE-079/examples/StoredXss.js | 13 ++++ .../CWE-079/examples/StoredXssGood.js | 14 +++++ .../security/dataflow/StoredXss.qll | 62 ++++++++++++++++++ .../Security/CWE-079/StoredXss.expected | 4 ++ .../Security/CWE-079/StoredXss.qlref | 1 + .../Security/CWE-079/xss-through-filenames.js | 40 ++++++++++++ 9 files changed, 218 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-079/StoredXss.qhelp create mode 100644 javascript/ql/src/Security/CWE-079/StoredXss.ql create mode 100644 javascript/ql/src/Security/CWE-079/examples/StoredXss.js create mode 100644 javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js create mode 100644 javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index 26debb63a24e..e55f89019f68 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -2,6 +2,7 @@ + semmlecode-javascript-queries/Security/CWE-022/TaintedPath.ql: /Security/CWE/CWE-022 + semmlecode-javascript-queries/Security/CWE-078/CommandInjection.ql: /Security/CWE/CWE-078 + semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079 ++ semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079 + semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089 + semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094 diff --git a/javascript/ql/src/Security/CWE-079/StoredXss.qhelp b/javascript/ql/src/Security/CWE-079/StoredXss.qhelp new file mode 100644 index 000000000000..1c3fde017989 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/StoredXss.qhelp @@ -0,0 +1,63 @@ + + + + +

    + + Directly using uncontrolled stored value (for example, file names) to + create HTML content without properly sanitizing the input first, + allows for a cross-site scripting vulnerability. + +

    +

    + + This kind of vulnerability is also called stored cross-site + scripting, to distinguish it from other types of cross-site scripting. + +

    +
    + + +

    + + To guard against cross-site scripting, consider using contextual + output encoding/escaping before using uncontrolled stored values to + create HTML content, or one of the other solutions that are mentioned + in the references. + +

    +
    + + +

    + + The following example code writes file names directly to a HTTP + response. This leaves the website vulnerable to cross-site scripting, + if an attacker can choose the file names on the disk. + +

    + +

    + Sanitizing the file names prevents the vulnerability: +

    + +
    + + +
  • + OWASP: + XSS + (Cross Site Scripting) Prevention Cheat Sheet. +
  • +
  • + OWASP + Types of Cross-Site + Scripting. +
  • +
  • + Wikipedia: Cross-site scripting. +
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-079/StoredXss.ql b/javascript/ql/src/Security/CWE-079/StoredXss.ql new file mode 100644 index 000000000000..429bccdf6601 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/StoredXss.ql @@ -0,0 +1,20 @@ +/** + * @name Stored cross-site scripting + * @description Using uncontrolled stored values in HTML allows for + * a stored cross-site scripting vulnerability. + * @kind problem + * @problem.severity error + * @precision high + * @id js/stored-xss + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-116 + */ + +import javascript +import semmle.javascript.security.dataflow.StoredXss::StoredXss + +from Configuration xss, DataFlow::Node source, DataFlow::Node sink +where xss.hasFlow(source, sink) +select sink, "Stored cross-site scripting vulnerability due to $@.", + source, "stored value" \ No newline at end of file diff --git a/javascript/ql/src/Security/CWE-079/examples/StoredXss.js b/javascript/ql/src/Security/CWE-079/examples/StoredXss.js new file mode 100644 index 000000000000..2ae6da6b4a27 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXss.js @@ -0,0 +1,13 @@ +var express = require('express'), + fs = require('fs'); + +express().get('/list-directory', function(req, res) { + fs.readdir('/public', function (error, fileNames) { + var list = '
      '; + fileNames.forEach(fileName => { + list += '
    • ' + fileName '
    • '; // BAD: `fileName` can contain HTML elements + }); + list += '
    ' + res.send(list); + }); +}); diff --git a/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js b/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js new file mode 100644 index 000000000000..61d18818e8ed --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js @@ -0,0 +1,14 @@ +var express = require('express'), + fs = require('fs'), + escape = require('escape-html'); + +express().get('/list-directory', function(req, res) { + fs.readdir('/public', function (error, fileNames) { + var list = '
      '; + fileNames.forEach(fileName => { + list += '
    • ' + escape(fileName) '
    • '; // GOOD: escaped `fileName` can not contain HTML elements + }); + list += '
    ' + res.send(list); + }); +}); diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll new file mode 100644 index 000000000000..8606d24af533 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll @@ -0,0 +1,62 @@ +/** + * Provides a taint-tracking configuration for reasoning about stored + * cross-site scripting vulnerabilities. + */ + +import javascript +import semmle.javascript.security.dataflow.RemoteFlowSources +import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss +import semmle.javascript.security.dataflow.DomBasedXss as DomBasedXss + +module StoredXss { + /** + * A data flow source for XSS vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for XSS vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for XSS vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A taint-tracking configuration for reasoning about XSS. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "StoredXss" } + + 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 + } + } + + /** A file name, considered as a flow source for stored XSS. */ + class FileNameSourceAsSource extends Source { + FileNameSourceAsSource() { + this instanceof FileNameSource + } + } + + /** An ordinary XSS sink, considered as a flow sink for stored XSS. */ + class XssSinkAsSink extends Sink { + XssSinkAsSink() { + this instanceof ReflectedXss::ReflectedXss::Sink or + this instanceof DomBasedXss::DomBasedXss::Sink + } + } + +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected new file mode 100644 index 000000000000..9d9a94ff27ea --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected @@ -0,0 +1,4 @@ +| xss-through-filenames.js:8:18:8:23 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:7:43:7:48 | files1 | stored value | +| xss-through-filenames.js:26:19:26:24 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value | +| xss-through-filenames.js:33:19:33:24 | files2 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value | +| xss-through-filenames.js:37:19:37:24 | files3 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref new file mode 100644 index 000000000000..27140feea760 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref @@ -0,0 +1 @@ +Security/CWE-079/StoredXss.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js b/javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js new file mode 100644 index 000000000000..c04e0d784efc --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js @@ -0,0 +1,40 @@ +var http = require('http'); +var fs = require('fs'); + +var express = require('express'); + +express().get('/', function(req, res) { + fs.readdir("/myDir", function (error, files1) { + res.send(files1); // NOT OK + }); +}); + +/** + * The essence of a real world vulnerability. + */ +http.createServer(function (req, res) { + + function format(files2) { + var files3 = []; + files2.sort(sort).forEach(function (file) { + files3.push('
  • ' + file + '
  • '); + }); + return files3.join(''); + } + + fs.readdir("/myDir", function (error, files1) { + res.write(files1); // NOT OK + + var dirs = []; + var files2 = []; + files1.forEach(function (file) { + files2.push(file); + }); + res.write(files2); // NOT OK + + var files3 = format(files2); + + res.write(files3); // NOT OK + + }); +}); From 5781b518bcdd65ba6e788b225f795f001990c25f Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 3 Sep 2018 15:53:42 +0200 Subject: [PATCH 057/110] JS: change notes for js/stored-xss --- change-notes/1.19/analysis-javascript.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 13a8680c27be..888e1e344c1e 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -4,11 +4,15 @@ * Modelling of taint flow through array operations has been improved. This may give additional results for the security queries. +* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features: + - file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby) + + ## New queries -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| -| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* | +| **Query** | **Tags** | **Purpose** | +|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on lgtm by default. | ## Changes to existing queries From 444a09a17c40f0e871f597a803ddf3e12cdef64a Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 5 Sep 2018 15:38:25 +0200 Subject: [PATCH 058/110] JS: add models of five file system libraries --- javascript/ql/src/javascript.qll | 1 + .../semmle/javascript/frameworks/Files.qll | 103 ++++++++++++++++++ .../Concepts/FileNameSource.expected | 12 ++ .../frameworks/Concepts/FileNameSource.ql | 3 + .../frameworks/Concepts/tst-file-names.js | 29 +++++ 5 files changed, 148 insertions(+) create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Files.qll create mode 100644 javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.expected create mode 100644 javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.ql create mode 100644 javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index 16bdff91f76a..a5a78a9ea5f5 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -59,6 +59,7 @@ import semmle.javascript.frameworks.Credentials import semmle.javascript.frameworks.CryptoLibraries import semmle.javascript.frameworks.DigitalOcean import semmle.javascript.frameworks.Electron +import semmle.javascript.frameworks.Files import semmle.javascript.frameworks.jQuery import semmle.javascript.frameworks.LodashUnderscore import semmle.javascript.frameworks.Logging diff --git a/javascript/ql/src/semmle/javascript/frameworks/Files.qll b/javascript/ql/src/semmle/javascript/frameworks/Files.qll new file mode 100644 index 000000000000..0138e8531fb9 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/Files.qll @@ -0,0 +1,103 @@ +/** + * Provides classes for working with file system libraries. + */ + +import javascript + +/** + * A file name from the `walk-sync` library. + */ +private class WalkSyncFileNameSource extends FileNameSource { + + WalkSyncFileNameSource() { + // `require('walkSync')()` + this = DataFlow::moduleImport("walkSync").getACall() + } + +} + +/** + * A file name or an array of file names from the `walk` library. + */ +private class WalkFileNameSource extends FileNameSource { + + WalkFileNameSource() { + // `stats.name` in `require('walk').walk(_).on(_, (_, stats) => stats.name)` + exists (DataFlow::FunctionNode callback | + callback = DataFlow::moduleMember("walk", "walk").getACall().getAMethodCall("on").getCallback(1) | + this = callback.getParameter(1).getAPropertyRead("name") + ) + } + +} + +/** + * A file name or an array of file names from the `glob` library. + */ +private class GlobFileNameSource extends FileNameSource { + + GlobFileNameSource() { + exists (string moduleName | + moduleName = "glob" | + // `require('glob').sync(_)` + this = DataFlow::moduleMember(moduleName, "sync").getACall() + or + // `name` in `require('glob')(_, (e, name) => ...)` + this = DataFlow::moduleImport(moduleName).getACall().getCallback([1..2]).getParameter(1) + or + exists (DataFlow::NewNode instance | + instance = DataFlow::moduleMember(moduleName, "Glob").getAnInstantiation() | + // `name` in `new require('glob').Glob(_, (e, name) => ...)` + this = instance.getCallback([1..2]).getParameter(1) or + // `new require('glob').Glob(_).found` + this = instance.getAPropertyRead("found") + ) + ) + } + +} + +/** + * A file name or an array of file names from the `globby` library. + */ +private class GlobbyFileNameSource extends FileNameSource { + + GlobbyFileNameSource() { + exists (string moduleName | + moduleName = "globby" | + // `require('globby').sync(_)` + this = DataFlow::moduleMember(moduleName, "sync").getACall() + or + // `files` in `require('globby')(_).then(files => ...)` + this = DataFlow::moduleImport(moduleName).getACall().getAMethodCall("then").getCallback(0).getParameter(0) + ) + } + +} + +/** + * A file name or an array of file names from the `fast-glob` library. + */ +private class FastGlobFileNameSource extends FileNameSource { + + FastGlobFileNameSource() { + exists (string moduleName | + moduleName = "fast-glob" | + // `require('fast-glob').sync(_)` + this = DataFlow::moduleMember(moduleName, "sync").getACall() + or + exists (DataFlow::SourceNode f | + f = DataFlow::moduleImport(moduleName) + or + f = DataFlow::moduleMember(moduleName, "async") | + // `files` in `require('fast-glob')(_).then(files => ...)` and + // `files` in `require('fast-glob').async(_).then(files => ...)` + this = f.getACall().getAMethodCall("then").getCallback(0).getParameter(0) + ) + or + // `file` in `require('fast-glob').stream(_).on(_, file => ...)` + this = DataFlow::moduleMember(moduleName, "stream").getACall().getAMethodCall("on").getCallback(1).getParameter(0) + ) + } + +} diff --git a/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.expected b/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.expected new file mode 100644 index 000000000000..bf05cf4163ef --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.expected @@ -0,0 +1,12 @@ +| tst-file-names.js:7:1:7:10 | walkSync() | +| tst-file-names.js:9:35:9:44 | stats.name | +| tst-file-names.js:11:1:11:12 | glob.sync(_) | +| tst-file-names.js:13:13:13:16 | name | +| tst-file-names.js:15:22:15:25 | name | +| tst-file-names.js:17:1:17:22 | new glo ... ).found | +| tst-file-names.js:19:1:19:14 | globby.sync(_) | +| tst-file-names.js:21:16:21:20 | files | +| tst-file-names.js:23:1:23:16 | fastGlob.sync(_) | +| tst-file-names.js:25:18:25:22 | files | +| tst-file-names.js:27:24:27:28 | files | +| tst-file-names.js:29:27:29:30 | file | diff --git a/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.ql b/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.ql new file mode 100644 index 000000000000..952897d58203 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Concepts/FileNameSource.ql @@ -0,0 +1,3 @@ +import javascript + +select any(FileNameSource s) \ No newline at end of file diff --git a/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js b/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js new file mode 100644 index 000000000000..293f2aa86d10 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Concepts/tst-file-names.js @@ -0,0 +1,29 @@ +let walkSync = require('walkSync'), + walk = require('walk'), + glob = require('glob'), + globby = require('globby'), + fastGlob = require('fast-glob'); + +walkSync(); + +walk.walk(_).on(_, (_, stats) => stats.name); // XXX + +glob.sync(_); + +glob(_, (e, name) => name); + +new glob.Glob(_, (e, name) => name); + +new glob.Glob(_).found; + +globby.sync(_); + +globby(_).then(files => files) + +fastGlob.sync(_); + +fastGlob(_).then(files => files); + +fastGlob.async(_).then(files => files); + +fastGlob.stream(_).on(_, file => file); // XXX From 7071c75567631e54ff33fa70f892cfab1e441050 Mon Sep 17 00:00:00 2001 From: Behrang Fouladi Azarnaminy Date: Fri, 14 Sep 2018 09:03:48 -0700 Subject: [PATCH 059/110] revert "Chaning EOL in two files" This reverts commit ecd08d4560a05a151044dcf858cfcf101c839911. --- .../Electron/NodeIntegration/EnablingNodeIntegration.qlref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref index 3f8dbad0d571..b0315fd89ad5 100644 --- a/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref @@ -1 +1 @@ -../../../../src/Electron/EnablingNodeIntegration.ql \ No newline at end of file +Electron/EnablingNodeIntegration.ql From bb48421d77788b8867ba90638dd4d4b4aeb7bb28 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 17 Sep 2018 11:08:35 +0200 Subject: [PATCH 060/110] JS: address doc review comments --- change-notes/1.19/analysis-javascript.md | 2 +- javascript/ql/src/Security/CWE-079/examples/StoredXss.js | 3 ++- javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 888e1e344c1e..e257a3658834 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -12,7 +12,7 @@ | **Query** | **Tags** | **Purpose** | |-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on lgtm by default. | +| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/ql/src/Security/CWE-079/examples/StoredXss.js b/javascript/ql/src/Security/CWE-079/examples/StoredXss.js index 2ae6da6b4a27..226cdced23d3 100644 --- a/javascript/ql/src/Security/CWE-079/examples/StoredXss.js +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXss.js @@ -5,7 +5,8 @@ express().get('/list-directory', function(req, res) { fs.readdir('/public', function (error, fileNames) { var list = '
      '; fileNames.forEach(fileName => { - list += '
    • ' + fileName '
    • '; // BAD: `fileName` can contain HTML elements + // BAD: `fileName` can contain HTML elements + list += '
    • ' + fileName '
    • '; }); list += '
    ' res.send(list); diff --git a/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js b/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js index 61d18818e8ed..0a05c3a7d452 100644 --- a/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js @@ -6,7 +6,8 @@ express().get('/list-directory', function(req, res) { fs.readdir('/public', function (error, fileNames) { var list = '
      '; fileNames.forEach(fileName => { - list += '
    • ' + escape(fileName) '
    • '; // GOOD: escaped `fileName` can not contain HTML elements + // GOOD: escaped `fileName` can not contain HTML elements + list += '
    • ' + escape(fileName) '
    • '; }); list += '
    ' res.send(list); From b20fd3c0843c96dfabbd62f9a7659237bcc88cb0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 17 Sep 2018 11:31:10 +0100 Subject: [PATCH 061/110] JS: recognize res.sendfile as alias for res.sendFile in Express --- javascript/ql/src/semmle/javascript/frameworks/Express.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Express.qll b/javascript/ql/src/semmle/javascript/frameworks/Express.qll index 9e9b52d050ee..c0163a143fe2 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Express.qll @@ -785,7 +785,8 @@ module Express { override MethodCallExpr astNode; ResponseSendFileAsFileSystemAccess() { - asExpr().(MethodCallExpr).calls(any(ResponseExpr res), "sendFile") + exists (string name | name = "sendFile" or name = "sendfile" | + asExpr().(MethodCallExpr).calls(any(ResponseExpr res), name)) } override DataFlow::Node getAPathArgument() { From e2cdf5d7ed7dd8b3b2d519c42c518d3a0fec733c Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 9 Aug 2018 14:53:31 +0100 Subject: [PATCH 062/110] JavaScript: add string concatenation library --- javascript/ql/src/javascript.qll | 1 + .../semmle/javascript/StringConcatenation.qll | 71 ++++++++++++++++ .../src/semmle/javascript/dataflow/Nodes.qll | 41 ++++++++++ .../javascript/dataflow/TaintTracking.qll | 15 +--- .../security/dataflow/DomBasedXss.qll | 15 ++-- .../dataflow/ServerSideUrlRedirect.qll | 18 +--- .../security/dataflow/UrlConcatenation.qll | 27 ++---- .../StringConcatenation/ContainsTwo.expected | 28 +++++++ .../StringConcatenation/ContainsTwo.ql | 15 ++++ .../library-tests/StringConcatenation/tst.js | 82 +++++++++++++++++++ 10 files changed, 259 insertions(+), 54 deletions(-) create mode 100644 javascript/ql/src/semmle/javascript/StringConcatenation.qll create mode 100644 javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected create mode 100644 javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.ql create mode 100644 javascript/ql/test/library-tests/StringConcatenation/tst.js diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index 16bdff91f76a..cdff23b3fe18 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -38,6 +38,7 @@ import semmle.javascript.Regexp import semmle.javascript.SSA import semmle.javascript.StandardLibrary import semmle.javascript.Stmt +import semmle.javascript.StringConcatenation import semmle.javascript.Templates import semmle.javascript.Tokens import semmle.javascript.TypeScript diff --git a/javascript/ql/src/semmle/javascript/StringConcatenation.qll b/javascript/ql/src/semmle/javascript/StringConcatenation.qll new file mode 100644 index 000000000000..bdbdd7871334 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/StringConcatenation.qll @@ -0,0 +1,71 @@ +/** + * Provides predicates for analyzing string concatenations and their operands. + */ +import javascript + +module StringConcatenation { + /** Gets a data flow node referring to the result of the given concatenation. */ + private DataFlow::Node getAssignAddResult(AssignAddExpr expr) { + result = expr.flow() + or + exists (SsaExplicitDefinition def | def.getDef() = expr | + result = DataFlow::valueNode(def.getVariable().getAUse())) + } + + /** Gets the `n`th operand to the string concatenation defining `node`. */ + DataFlow::Node getOperand(DataFlow::Node node, int n) { + exists (AddExpr add | node = add.flow() | + n = 0 and result = add.getLeftOperand().flow() + or + n = 1 and result = add.getRightOperand().flow()) + or + exists (TemplateLiteral template | node = template.flow() | + result = template.getElement(n).flow() and + not exists (TaggedTemplateExpr tag | template = tag.getTemplate())) + or + exists (AssignAddExpr assign | node = getAssignAddResult(assign) | + n = 0 and result = assign.getLhs().flow() + or + n = 1 and result = assign.getRhs().flow()) + or + exists (DataFlow::ArrayCreationNode array | + node = array.getAMethodCall("join") and + node.(DataFlow::MethodCallNode).getArgument(0).mayHaveStringValue("") and + result = array.getElement(n)) + } + + /** Gets an operand to the string concatenation defining `node`. */ + DataFlow::Node getAnOperand(DataFlow::Node node) { + result = getOperand(node, _) + } + + /** Gets the number of operands to the given concatenation. */ + int getNumOperand(DataFlow::Node node) { + result = strictcount(getAnOperand(node)) + } + + /** Gets the first operand to the string concatenation defining `node`. */ + DataFlow::Node getFirstOperand(DataFlow::Node node) { + result = getOperand(node, 0) + } + + /** Gets the last operand to the string concatenation defining `node`. */ + DataFlow::Node getLastOperand(DataFlow::Node node) { + result = getOperand(node, getNumOperand(node) - 1) + } + + /** + * Holds if `src` flows to `dst` through the `n`th operand of the given concatenation operator. + */ + predicate taintStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::Node operator, int n) { + src = getOperand(dst, n) and + operator = dst + } + + /** + * Holds if there is a taint step from `src` to `dst` through string concatenation. + */ + predicate taintStep(DataFlow::Node src, DataFlow::Node dst) { + taintStep(src, dst, _, _) + } +} diff --git a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll index 78d819d490be..facd7aca137d 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll @@ -304,6 +304,47 @@ class ArrayLiteralNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode } +/** A data flow node corresponding to a `new Array()` or `Array()` invocation. */ +class ArrayConstructorInvokeNode extends DataFlow::InvokeNode { + ArrayConstructorInvokeNode() { + getCallee() = DataFlow::globalVarRef("Array") + } + + /** Gets the `i`th initial element of this array, if one is provided. */ + DataFlow::ValueNode getElement(int i) { + getNumArgument() > 1 and // A single-argument invocation specifies the array length, not an element. + result = getArgument(i) + } + + /** Gets an initial element of this array, if one is provided. */ + DataFlow::ValueNode getAnElement() { + getNumArgument() > 1 and + result = getAnArgument() + } +} + +/** + * A data flow node corresponding to the creation or a new array, either through an array literal + * or an invocation of the `Array` constructor. + */ +class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode { + ArrayCreationNode() { + this instanceof ArrayLiteralNode or + this instanceof ArrayConstructorInvokeNode + } + + /** Gets the `i`th initial element of this array, if one is provided. */ + DataFlow::ValueNode getElement(int i) { + result = this.(ArrayLiteralNode).getElement(i) or + result = this.(ArrayConstructorInvokeNode).getElement(i) + } + + /** Gets an initial element of this array, if one if provided. */ + DataFlow::ValueNode getAnElement() { + result = getElement(_) + } +} + /** * A data flow node corresponding to a `default` import from a module, or a * (AMD or CommonJS) `require` of a module. diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 55f8ec8b9e7e..1e6e47d254d9 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -358,19 +358,8 @@ module TaintTracking { */ class StringConcatenationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - succ = this and - ( - // addition propagates taint - astNode.(AddExpr).getAnOperand() = pred.asExpr() or - astNode.(AssignAddExpr).getAChildExpr() = pred.asExpr() or - exists (SsaExplicitDefinition ssa | - astNode = ssa.getVariable().getAUse() and - pred.asExpr().(AssignAddExpr) = ssa.getDef() - ) - or - // templating propagates taint - astNode.(TemplateLiteral).getAnElement() = pred.asExpr() - ) + succ = this and + StringConcatenation::taintStep(pred, succ) } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll index dc248e2282e8..5df5c1436e63 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll @@ -77,9 +77,9 @@ module DomBasedXss { or // or it doesn't start with something other than `<`, and so at least // _may_ be interpreted as HTML - not exists (Expr prefix, string strval | + not exists (DataFlow::Node prefix, string strval | isPrefixOfJQueryHtmlString(astNode, prefix) and - strval = prefix.getStringValue() and + strval = prefix.asExpr().getStringValue() and not strval.regexpMatch("\\s*<.*") ) ) @@ -93,13 +93,14 @@ module DomBasedXss { * Holds if `prefix` is a prefix of `htmlString`, which may be intepreted as * HTML by a jQuery method. */ - private predicate isPrefixOfJQueryHtmlString(Expr htmlString, Expr prefix) { + private predicate isPrefixOfJQueryHtmlString(Expr htmlString, DataFlow::Node prefix) { any(JQueryMethodCall call).interpretsArgumentAsHtml(htmlString) and - prefix = htmlString + prefix = htmlString.flow() or - exists (Expr pred | isPrefixOfJQueryHtmlString(htmlString, pred) | - prefix = pred.(AddExpr).getLeftOperand() or - prefix = pred.(ParExpr).getExpression() + exists (DataFlow::Node pred | isPrefixOfJQueryHtmlString(htmlString, pred) | + prefix = StringConcatenation::getFirstOperand(pred) + or + prefix = pred.getAPredecessor() ) } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll index e45d8ddadbc4..7836d2c594ea 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirect.qll @@ -34,33 +34,23 @@ module ServerSideUrlRedirect { ) } } - - /** - * Gets the left operand of `nd` if it is a concatenation. - */ - private DataFlow::Node getPrefixOperand(DataFlow::Node nd) { - exists (Expr e | e instanceof AddExpr or e instanceof AssignAddExpr | - nd = DataFlow::valueNode(e) and - result = DataFlow::valueNode(e.getChildExpr(0)) - ) - } /** * Gets a node that is transitively reachable from `nd` along prefix predecessor edges. */ private DataFlow::Node prefixCandidate(Sink sink) { result = sink or - result = getPrefixOperand(prefixCandidate(sink)) or - result = prefixCandidate(sink).getAPredecessor() + result = prefixCandidate(sink).getAPredecessor() or + result = StringConcatenation::getFirstOperand(prefixCandidate(sink)) } - + /** * Gets an expression that may end up being a prefix of the string concatenation `nd`. */ private Expr getAPrefix(Sink sink) { exists (DataFlow::Node prefix | prefix = prefixCandidate(sink) and - not exists(getPrefixOperand(prefix)) and + not exists(StringConcatenation::getFirstOperand(prefix)) and not exists(prefix.getAPredecessor()) and result = prefix.asExpr() ) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll index 80be600def2b..5d12531128ab 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UrlConcatenation.qll @@ -11,16 +11,13 @@ import javascript * `nd` or one of its operands, assuming that it is a concatenation. */ private predicate hasSanitizingSubstring(DataFlow::Node nd) { - exists (Expr e | e = nd.asExpr() | - (e instanceof AddExpr or e instanceof AssignAddExpr) and - hasSanitizingSubstring(DataFlow::valueNode(e.getAChildExpr())) - or - e.getStringValue().regexpMatch(".*[?#].*") - ) + nd.asExpr().getStringValue().regexpMatch(".*[?#].*") or - nd.isIncomplete(_) + hasSanitizingSubstring(StringConcatenation::getAnOperand(nd)) or hasSanitizingSubstring(nd.getAPredecessor()) + or + nd.isIncomplete(_) } /** @@ -30,17 +27,7 @@ private predicate hasSanitizingSubstring(DataFlow::Node nd) { * This is considered as a sanitizing edge for the URL redirection queries. */ predicate sanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) { - exists (AddExpr add, DataFlow::Node left | - source.asExpr() = add.getRightOperand() and - sink.asExpr() = add and - left.asExpr() = add.getLeftOperand() and - hasSanitizingSubstring(left) - ) - or - exists (TemplateLiteral tl, int i, DataFlow::Node elt | - source.asExpr() = tl.getElement(i) and - sink.asExpr() = tl and - elt.asExpr() = tl.getElement([0..i-1]) and - hasSanitizingSubstring(elt) - ) + exists (DataFlow::Node operator, int n | + StringConcatenation::taintStep(source, sink, operator, n) and + hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0..n-1]))) } diff --git a/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected new file mode 100644 index 000000000000..8d28a0c88322 --- /dev/null +++ b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected @@ -0,0 +1,28 @@ +| tst.js:3:3:3:12 | x += "two" | +| tst.js:3:8:3:12 | "two" | +| tst.js:4:3:4:3 | x | +| tst.js:4:3:4:14 | x += "three" | +| tst.js:5:3:5:3 | x | +| tst.js:5:3:5:13 | x += "four" | +| tst.js:6:10:6:10 | x | +| tst.js:12:5:12:26 | x += "o ... + "two" | +| tst.js:12:10:12:26 | "one" + y + "two" | +| tst.js:12:22:12:26 | "two" | +| tst.js:19:11:19:23 | "one" + "two" | +| tst.js:19:19:19:23 | "two" | +| tst.js:20:3:20:3 | x | +| tst.js:20:3:20:25 | x += (" ... "four") | +| tst.js:21:10:21:10 | x | +| tst.js:21:10:21:19 | x + "five" | +| tst.js:25:10:25:41 | ["one", ... oin("") | +| tst.js:25:18:25:22 | "two" | +| tst.js:29:10:29:46 | Array(" ... oin("") | +| tst.js:29:23:29:27 | "two" | +| tst.js:33:10:33:50 | new Arr ... oin("") | +| tst.js:33:27:33:31 | "two" | +| tst.js:38:11:38:15 | "two" | +| tst.js:46:23:46:27 | "two" | +| tst.js:53:10:53:34 | `one ${ ... three` | +| tst.js:53:19:53:23 | two | +| tst.js:71:14:71:18 | "two" | +| tst.js:77:23:77:27 | "two" | diff --git a/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.ql b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.ql new file mode 100644 index 000000000000..2e4c558414d1 --- /dev/null +++ b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.ql @@ -0,0 +1,15 @@ +import javascript + +// Select all expressions whose string value contains the word "two" + +predicate containsTwo(DataFlow::Node node) { + node.asExpr().getStringValue().regexpMatch(".*two.*") + or + containsTwo(node.getAPredecessor()) + or + containsTwo(StringConcatenation::getAnOperand(node)) +} + +from Expr e +where containsTwo(e.flow()) +select e diff --git a/javascript/ql/test/library-tests/StringConcatenation/tst.js b/javascript/ql/test/library-tests/StringConcatenation/tst.js new file mode 100644 index 000000000000..f9f42c33f17b --- /dev/null +++ b/javascript/ql/test/library-tests/StringConcatenation/tst.js @@ -0,0 +1,82 @@ +function append() { + let x = "one"; + x += "two"; + x += "three" + x += "four" + return x; +} + +function appendClosure(ys) { + let x = "first"; + ys.forEach(y => { + x += "one" + y + "two"; + }); + x += "last"; + return x; +} + +function appendMixed() { + let x = "one" + "two"; + x += ("three" + "four"); + return x + "five"; +} + +function joinArrayLiteral() { + return ["one", "two", "three"].join(""); +} + +function joinArrayCall() { + return Array("one", "two", "three").join(""); +} + +function joinArrayNewCall() { + return new Array("one", "two", "three").join(""); +} + +function push() { + let xs = ["one"]; + xs.push("two"); + xs.push("three", "four"); + return xs.join(""); +} + +function pushClosure(ys) { + let xs = ["first"]; + ys.forEach(y => { + xs.push("one", y, "two"); + }); + xs.push("last"); + return xs.join(""); +} + +function template(x) { + return `one ${x} two ${x} three`; +} + +function taggedTemplate(mid) { + return someTag`first ${mid} last`; +} + +function templateRepeated(x) { + return `first ${x}${x}${x} last`; +} + +function makeArray() { + return []; +} + +function pushNoLocalCreation() { + let array = makeArray(); + array.push("one"); + array.push("two"); + array.push("three"); + return array.join(""); +} + +function joinInClosure() { + let array = ["one", "two", "three"]; + function f() { + return array.join(); + } + return f(); +} From 9384b85bcca20f3407a985c4e123360f80e3f142 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 17 Sep 2018 14:31:26 +0100 Subject: [PATCH 063/110] JavaScript: ensure prefix sanitizers work for array.join() --- .../semmle/javascript/StringConcatenation.qll | 17 +++++++++++++---- .../StringConcatenation/ContainsTwo.expected | 3 +++ .../ServerSideUrlRedirect.expected | 1 + .../CWE-601/ServerSideUrlRedirect/express.js | 11 +++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/StringConcatenation.qll b/javascript/ql/src/semmle/javascript/StringConcatenation.qll index bdbdd7871334..50e06860bd06 100644 --- a/javascript/ql/src/semmle/javascript/StringConcatenation.qll +++ b/javascript/ql/src/semmle/javascript/StringConcatenation.qll @@ -28,10 +28,19 @@ module StringConcatenation { or n = 1 and result = assign.getRhs().flow()) or - exists (DataFlow::ArrayCreationNode array | - node = array.getAMethodCall("join") and - node.(DataFlow::MethodCallNode).getArgument(0).mayHaveStringValue("") and - result = array.getElement(n)) + exists (DataFlow::ArrayCreationNode array, DataFlow::MethodCallNode call | + call = array.getAMethodCall("join") and + call.getArgument(0).mayHaveStringValue("") and + ( + // step from array element to array + result = array.getElement(n) and + node = array + or + // step from array to join call + node = call and + result = array and + n = 0 + )) } /** Gets an operand to the string concatenation defining `node`. */ diff --git a/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected index 8d28a0c88322..841e0fad9e32 100644 --- a/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected +++ b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected @@ -14,10 +14,13 @@ | tst.js:20:3:20:25 | x += (" ... "four") | | tst.js:21:10:21:10 | x | | tst.js:21:10:21:19 | x + "five" | +| tst.js:25:10:25:32 | ["one", ... three"] | | tst.js:25:10:25:41 | ["one", ... oin("") | | tst.js:25:18:25:22 | "two" | +| tst.js:29:10:29:37 | Array(" ... three") | | tst.js:29:10:29:46 | Array(" ... oin("") | | tst.js:29:23:29:27 | "two" | +| tst.js:33:10:33:41 | new Arr ... three") | | tst.js:33:10:33:50 | new Arr ... oin("") | | tst.js:33:27:33:31 | "two" | | tst.js:38:11:38:15 | "two" | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected index 7affeef3b058..2bf3cc7dd5b3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected @@ -6,6 +6,7 @@ | express.js:78:16:78:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:78:19:78:37 | req.param("target") | user-provided value | | express.js:94:18:94:23 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value | | express.js:101:16:101:21 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value | +| express.js:119:16:119:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:119:17:119:30 | req.query.page | user-provided value | | node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value | | node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value | | node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js index 7ae7bbb3d900..2a62c7a9e5c0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/express.js @@ -110,3 +110,14 @@ app.get('/some/path', function(req, res) { else res.redirect(target); }); + +app.get('/array/join', function(req, res) { + // GOOD: request input embedded in query string + res.redirect(['index.html?section=', req.query.section].join('')); + + // GOOD: request input still embedded in query string + res.redirect(['index.html?section=', '34'].join('') + '&subsection=' + req.query.subsection); + + // BAD: request input becomes before query string + res.redirect([req.query.page, '?section=', req.query.section].join('')); +}); From 46b2c19c66fe022c005260eea79699ccd7fca78d Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Mon, 17 Sep 2018 17:19:05 -0700 Subject: [PATCH 064/110] C++: Initial attempt at IR-based value numbering --- config/identical-files.json | 5 + cpp/ql/src/semmle/code/cpp/ir/IR.md | 7 + cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll | 1 + .../cpp/ir/implementation/aliased_ssa/IR.qll | 27 + .../aliased_ssa/Instruction.qll | 16 + .../ir/implementation/aliased_ssa/PrintIR.qll | 22 + .../aliased_ssa/internal/SSAConstruction.qll | 4 + .../aliased_ssa/internal/gvn/ValueNumber.qll | 264 +++++++ .../internal/gvn/ValueNumberInternal.qll | 1 + .../code/cpp/ir/implementation/raw/IR.qll | 27 + .../cpp/ir/implementation/raw/Instruction.qll | 16 + .../cpp/ir/implementation/raw/PrintIR.qll | 22 + .../raw/internal/IRConstruction.qll | 8 + .../raw/internal/gvn/ValueNumber.qll | 264 +++++++ .../raw/internal/gvn/ValueNumberInternal.qll | 1 + .../ir/implementation/unaliased_ssa/IR.qll | 27 + .../unaliased_ssa/Instruction.qll | 16 + .../implementation/unaliased_ssa/PrintIR.qll | 22 + .../internal/SSAConstruction.qll | 4 + .../internal/gvn/ValueNumber.qll | 264 +++++++ .../internal/gvn/ValueNumberInternal.qll | 1 + .../GlobalValueNumbering/ir_gvn.expected | 664 ++++++++++++++++++ .../GlobalValueNumbering/ir_gvn.ql | 6 + 23 files changed, 1689 insertions(+) create mode 100644 cpp/ql/src/semmle/code/cpp/ir/IR.md create mode 100644 cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll create mode 100644 cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll create mode 100644 cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected create mode 100644 cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql diff --git a/config/identical-files.json b/config/identical-files.json index bb1c5dca83ca..c7c6ba4bc01f 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -54,5 +54,10 @@ "C++ SSA SSAConstruction": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" + ], + "C++ IR ValueNumber": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll" ] } diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.md b/cpp/ql/src/semmle/code/cpp/ir/IR.md new file mode 100644 index 000000000000..e40e04d4a3d2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/IR.md @@ -0,0 +1,7 @@ +# The C/C++ Intermediate Representation + +## Introduction + +The Intermediate Representation (IR) library provides a representation of the semantics of the program, independent of the syntax used to express those semantics. The IR is similar in design to representations used in optimizing compilers, such as LLVM IR. + +## Memory Access diff --git a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll new file mode 100644 index 000000000000..3ff802376357 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll @@ -0,0 +1 @@ +import implementation.aliased_ssa.PrintIR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index 26813be359cb..5e84762e7eb8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -5,3 +5,30 @@ import IRVariable import OperandTag import semmle.code.cpp.ir.implementation.EdgeKind import semmle.code.cpp.ir.implementation.MemoryAccessKind + +private newtype TIRPropertyProvider = MkIRPropertyProvider() + +/** + * Class that provides additional properties to be dumped for IR instructions and blocks when using + * the PrintIR module. Libraries that compute additional facts about IR elements can extend the + * single instance of this class to specify the additional properties computed by the library. + */ +class IRPropertyProvider extends TIRPropertyProvider { + string toString() { + result = "IRPropertyProvider" + } + + /** + * Gets the value of the property named `key` for the specified instruction. + */ + string getInstructionProperty(Instruction instruction, string key) { + none() + } + + /** + * Gets the value of the property named `key` for the specified block. + */ + string getBlockProperty(IRBlock block, string key) { + none() + } +} 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 68a3e83d2db0..a7046ce467d1 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 @@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction { result = ast.getLocation() } + /** + * Gets the `Expr` whose results is computed by this instruction, if any. + */ + final Expr getResultExpression() { + result = Construction::getInstructionResultExpression(this) + } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction { } } +/** + * An instruction that initializes the `this` pointer parameter of the enclosing function. + */ +class InitializeThisInstruction extends Instruction { + InitializeThisInstruction() { + opcode instanceof Opcode::InitializeThis + } +} + class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { opcode instanceof Opcode::FieldAddress diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index e745b20529a9..478e92fac5c9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -1,6 +1,18 @@ private import IR import cpp +private string getAdditionalInstructionProperty(Instruction instr, string key) { + exists(IRPropertyProvider provider | + result = provider.getInstructionProperty(instr, key) + ) +} + +private string getAdditionalBlockProperty(IRBlock block, string key) { + exists(IRPropertyProvider provider | + result = provider.getBlockProperty(block, key) + ) +} + private newtype TPrintableIRNode = TPrintableFunctionIR(FunctionIR funcIR) or TPrintableIRBlock(IRBlock block) or @@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { result.getFunctionIR() = block.getFunctionIR() } + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalBlockProperty(block, key) + } + final IRBlock getBlock() { result = block } @@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { final Instruction getInstruction() { result = instr } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalInstructionProperty(instr, key) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { 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 aad29a8c442b..4a992ffb4ce0 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 @@ -195,6 +195,10 @@ cached private module Cached { ) } + cached Expr getInstructionResultExpression(Instruction instruction) { + result = getOldInstruction(instruction).getResultExpression() + } + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll new file mode 100644 index 000000000000..e682c417ba2a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll @@ -0,0 +1,264 @@ +private import ValueNumberInternal +import cpp +private import IR + +/** + * Provides additional information about value numbering in IR dumps. + */ +class ValueNumberPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + exists(ValueNumber vn | + vn = valueNumber(instr) and + key = "valnum" and + if strictcount(vn.getAnInstruction()) > 1 then + result = vn.toString() + else + result = "unique" + ) + } +} + +newtype TValueNumber = + TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) { + variableAddressValueNumber(_, funcIR, var) + } or + TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) { + initializeParameterValueNumber(_, funcIR, var) + } or + TInitializeThisValueNumber(FunctionIR funcIR) { + initializeThisValueNumber(_, funcIR) + } or + TConstantValueNumber(FunctionIR funcIR, Type type, string value) { + constantValueNumber(_, funcIR, type, value) + } or + TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) { + fieldAddressValueNumber(_, funcIR, field, objectAddress) + } or + TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand, + ValueNumber rightOperand) { + binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand) + } or + TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize, + ValueNumber leftOperand, ValueNumber rightOperand) { + pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand) + } or + TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { + unaryValueNumber(_, funcIR, opcode, type, operand) + } or + TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { + uniqueValueNumber(instr, funcIR) + } + +/** + * The value number assigned to a particular set of instructions that produce equivalent results. + */ +class ValueNumber extends TValueNumber { + final string toString() { + result = getExampleInstruction().getResultId() + } + + final Location getLocation() { + result = getExampleInstruction().getLocation() + } + + /** + * Gets the instructions that have been assigned this value number. This will always produce at + * least one result. + */ + final Instruction getAnInstruction() { + this = valueNumber(result) + } + + /** + * Gets one of the instructions that was assigned this value number. The chosen instuction is + * deterministic but arbitrary. Intended for use only in debugging. + */ + final Instruction getExampleInstruction() { + result = min(Instruction instr | + instr = getAnInstruction() | + instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() + ) + } +} + +class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} +class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} +class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} +class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} +class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} +class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} + +/** + * A `CopyInstruction` whose source operand's value is congruent to the definition of that source + * operand. + * For example: + * ``` + * Point p = { 1, 2 }; + * Point q = p; + * int a = p.x; + * ``` + * The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that + * definition because it accesses the exact same memory. + * The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not + * congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`. + * + * This concept should probably be exposed in the public IR API. + */ +private class CongruentCopyInstruction extends CopyInstruction { + CongruentCopyInstruction() { + exists(Instruction def | + def = this.getSourceValue() and + ( + def.getResultMemoryAccess() instanceof IndirectMemoryAccess or + not def.hasMemoryResult() + ) + ) + } +} + +/** + * Holds if this library knows how to assign a value number to the specified instruction, other than + * a `unique` value number that is never shared by multiple instructions. + */ +private predicate numberableInstruction(Instruction instr) { + instr instanceof VariableAddressInstruction or + instr instanceof InitializeParameterInstruction or + instr instanceof InitializeThisInstruction or + instr instanceof ConstantInstruction or + instr instanceof FieldAddressInstruction or + instr instanceof BinaryInstruction or + instr instanceof UnaryInstruction or + instr instanceof PointerArithmeticInstruction or + instr instanceof CongruentCopyInstruction +} + +private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR, + IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeParameterValueNumber(InitializeParameterInstruction instr, + FunctionIR funcIR, IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR +} + +private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type, + string value) { + instr.getFunctionIR() = funcIR and + instr.getResultType() = type and + instr.getValue() = value +} + +private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR, + Field field, ValueNumber objectAddress) { + instr.getFunctionIR() = funcIR and + instr.getField() = field and + valueNumber(instr.getObjectAddress()) = objectAddress +} + +private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber leftOperand, ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof PointerArithmeticInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr, + FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getResultType() = type and + instr.getElementSize() = elementSize and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof InheritanceConversionInstruction) and + (not instr instanceof FieldAddressInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getOperand()) = operand +} + +/** + * Holds if `instr` should be assigned a unique value number because this library does not know how + * to determine if two instances of that instruction are equivalent. + */ +private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR and + (not instr.getResultType() instanceof VoidType) and + not numberableInstruction(instr) +} + +/** + * Gets the value number assigned to `instr`, if any. Returns at most one result. + */ +ValueNumber valueNumber(Instruction instr) { + result = nonUniqueValueNumber(instr) or + exists(FunctionIR funcIR | + uniqueValueNumber(instr, funcIR) and + result = TUniqueValueNumber(funcIR, instr) + ) +} + +/** + * Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique + * value number. + */ +private ValueNumber nonUniqueValueNumber(Instruction instr) { + exists(FunctionIR funcIR | + funcIR = instr.getFunctionIR() and + ( + exists(IRVariable var | + variableAddressValueNumber(instr, funcIR, var) and + result = TVariableAddressValueNumber(funcIR, var) + ) or + exists(IRVariable var | + initializeParameterValueNumber(instr, funcIR, var) and + result = TInitializeParameterValueNumber(funcIR, var) + ) or + ( + initializeThisValueNumber(instr, funcIR) and + result = TInitializeThisValueNumber(funcIR) + ) or + exists(Type type, string value | + constantValueNumber(instr, funcIR, type, value) and + result = TConstantValueNumber(funcIR, type, value) + ) or + exists(Field field, ValueNumber objectAddress | + fieldAddressValueNumber(instr, funcIR, field, objectAddress) and + result = TFieldAddressValueNumber(funcIR, field, objectAddress) + ) or + exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and + result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand) + ) or + exists(Opcode opcode, Type type, ValueNumber operand | + unaryValueNumber(instr, funcIR, opcode, type, operand) and + result = TUnaryValueNumber(funcIR, opcode, type, operand) + ) or + exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | + pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, + rightOperand) and + result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand, + rightOperand) + ) or + // The value number of a copy is just the value number of its source value. + result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue()) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll new file mode 100644 index 000000000000..d55844c04712 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index 26813be359cb..5e84762e7eb8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -5,3 +5,30 @@ import IRVariable import OperandTag import semmle.code.cpp.ir.implementation.EdgeKind import semmle.code.cpp.ir.implementation.MemoryAccessKind + +private newtype TIRPropertyProvider = MkIRPropertyProvider() + +/** + * Class that provides additional properties to be dumped for IR instructions and blocks when using + * the PrintIR module. Libraries that compute additional facts about IR elements can extend the + * single instance of this class to specify the additional properties computed by the library. + */ +class IRPropertyProvider extends TIRPropertyProvider { + string toString() { + result = "IRPropertyProvider" + } + + /** + * Gets the value of the property named `key` for the specified instruction. + */ + string getInstructionProperty(Instruction instruction, string key) { + none() + } + + /** + * Gets the value of the property named `key` for the specified block. + */ + string getBlockProperty(IRBlock block, string key) { + none() + } +} 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 68a3e83d2db0..a7046ce467d1 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 @@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction { result = ast.getLocation() } + /** + * Gets the `Expr` whose results is computed by this instruction, if any. + */ + final Expr getResultExpression() { + result = Construction::getInstructionResultExpression(this) + } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction { } } +/** + * An instruction that initializes the `this` pointer parameter of the enclosing function. + */ +class InitializeThisInstruction extends Instruction { + InitializeThisInstruction() { + opcode instanceof Opcode::InitializeThis + } +} + class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { opcode instanceof Opcode::FieldAddress diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index e745b20529a9..478e92fac5c9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -1,6 +1,18 @@ private import IR import cpp +private string getAdditionalInstructionProperty(Instruction instr, string key) { + exists(IRPropertyProvider provider | + result = provider.getInstructionProperty(instr, key) + ) +} + +private string getAdditionalBlockProperty(IRBlock block, string key) { + exists(IRPropertyProvider provider | + result = provider.getBlockProperty(block, key) + ) +} + private newtype TPrintableIRNode = TPrintableFunctionIR(FunctionIR funcIR) or TPrintableIRBlock(IRBlock block) or @@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { result.getFunctionIR() = block.getFunctionIR() } + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalBlockProperty(block, key) + } + final IRBlock getBlock() { result = block } @@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { final Instruction getInstruction() { result = instr } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalInstructionProperty(instr, key) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 04fe1d87b620..306180e764ec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -4,6 +4,7 @@ import IRBlockConstruction as BlockConstruction private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedElement +private import TranslatedExpr private import TranslatedFunction class InstructionTagType extends TInstructionTag { @@ -73,6 +74,13 @@ cached private module Cached { none() } + cached Expr getInstructionResultExpression(Instruction instruction) { + exists(TranslatedExpr translatedExpr | + translatedExpr = getTranslatedExpr(result) and + instruction = translatedExpr.getResult() + ) + } + cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) { result = getInstructionTranslatedElement(instruction).getInstructionOperand( instruction.getTag(), tag) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll new file mode 100644 index 000000000000..e682c417ba2a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll @@ -0,0 +1,264 @@ +private import ValueNumberInternal +import cpp +private import IR + +/** + * Provides additional information about value numbering in IR dumps. + */ +class ValueNumberPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + exists(ValueNumber vn | + vn = valueNumber(instr) and + key = "valnum" and + if strictcount(vn.getAnInstruction()) > 1 then + result = vn.toString() + else + result = "unique" + ) + } +} + +newtype TValueNumber = + TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) { + variableAddressValueNumber(_, funcIR, var) + } or + TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) { + initializeParameterValueNumber(_, funcIR, var) + } or + TInitializeThisValueNumber(FunctionIR funcIR) { + initializeThisValueNumber(_, funcIR) + } or + TConstantValueNumber(FunctionIR funcIR, Type type, string value) { + constantValueNumber(_, funcIR, type, value) + } or + TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) { + fieldAddressValueNumber(_, funcIR, field, objectAddress) + } or + TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand, + ValueNumber rightOperand) { + binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand) + } or + TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize, + ValueNumber leftOperand, ValueNumber rightOperand) { + pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand) + } or + TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { + unaryValueNumber(_, funcIR, opcode, type, operand) + } or + TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { + uniqueValueNumber(instr, funcIR) + } + +/** + * The value number assigned to a particular set of instructions that produce equivalent results. + */ +class ValueNumber extends TValueNumber { + final string toString() { + result = getExampleInstruction().getResultId() + } + + final Location getLocation() { + result = getExampleInstruction().getLocation() + } + + /** + * Gets the instructions that have been assigned this value number. This will always produce at + * least one result. + */ + final Instruction getAnInstruction() { + this = valueNumber(result) + } + + /** + * Gets one of the instructions that was assigned this value number. The chosen instuction is + * deterministic but arbitrary. Intended for use only in debugging. + */ + final Instruction getExampleInstruction() { + result = min(Instruction instr | + instr = getAnInstruction() | + instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() + ) + } +} + +class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} +class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} +class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} +class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} +class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} +class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} + +/** + * A `CopyInstruction` whose source operand's value is congruent to the definition of that source + * operand. + * For example: + * ``` + * Point p = { 1, 2 }; + * Point q = p; + * int a = p.x; + * ``` + * The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that + * definition because it accesses the exact same memory. + * The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not + * congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`. + * + * This concept should probably be exposed in the public IR API. + */ +private class CongruentCopyInstruction extends CopyInstruction { + CongruentCopyInstruction() { + exists(Instruction def | + def = this.getSourceValue() and + ( + def.getResultMemoryAccess() instanceof IndirectMemoryAccess or + not def.hasMemoryResult() + ) + ) + } +} + +/** + * Holds if this library knows how to assign a value number to the specified instruction, other than + * a `unique` value number that is never shared by multiple instructions. + */ +private predicate numberableInstruction(Instruction instr) { + instr instanceof VariableAddressInstruction or + instr instanceof InitializeParameterInstruction or + instr instanceof InitializeThisInstruction or + instr instanceof ConstantInstruction or + instr instanceof FieldAddressInstruction or + instr instanceof BinaryInstruction or + instr instanceof UnaryInstruction or + instr instanceof PointerArithmeticInstruction or + instr instanceof CongruentCopyInstruction +} + +private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR, + IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeParameterValueNumber(InitializeParameterInstruction instr, + FunctionIR funcIR, IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR +} + +private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type, + string value) { + instr.getFunctionIR() = funcIR and + instr.getResultType() = type and + instr.getValue() = value +} + +private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR, + Field field, ValueNumber objectAddress) { + instr.getFunctionIR() = funcIR and + instr.getField() = field and + valueNumber(instr.getObjectAddress()) = objectAddress +} + +private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber leftOperand, ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof PointerArithmeticInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr, + FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getResultType() = type and + instr.getElementSize() = elementSize and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof InheritanceConversionInstruction) and + (not instr instanceof FieldAddressInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getOperand()) = operand +} + +/** + * Holds if `instr` should be assigned a unique value number because this library does not know how + * to determine if two instances of that instruction are equivalent. + */ +private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR and + (not instr.getResultType() instanceof VoidType) and + not numberableInstruction(instr) +} + +/** + * Gets the value number assigned to `instr`, if any. Returns at most one result. + */ +ValueNumber valueNumber(Instruction instr) { + result = nonUniqueValueNumber(instr) or + exists(FunctionIR funcIR | + uniqueValueNumber(instr, funcIR) and + result = TUniqueValueNumber(funcIR, instr) + ) +} + +/** + * Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique + * value number. + */ +private ValueNumber nonUniqueValueNumber(Instruction instr) { + exists(FunctionIR funcIR | + funcIR = instr.getFunctionIR() and + ( + exists(IRVariable var | + variableAddressValueNumber(instr, funcIR, var) and + result = TVariableAddressValueNumber(funcIR, var) + ) or + exists(IRVariable var | + initializeParameterValueNumber(instr, funcIR, var) and + result = TInitializeParameterValueNumber(funcIR, var) + ) or + ( + initializeThisValueNumber(instr, funcIR) and + result = TInitializeThisValueNumber(funcIR) + ) or + exists(Type type, string value | + constantValueNumber(instr, funcIR, type, value) and + result = TConstantValueNumber(funcIR, type, value) + ) or + exists(Field field, ValueNumber objectAddress | + fieldAddressValueNumber(instr, funcIR, field, objectAddress) and + result = TFieldAddressValueNumber(funcIR, field, objectAddress) + ) or + exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and + result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand) + ) or + exists(Opcode opcode, Type type, ValueNumber operand | + unaryValueNumber(instr, funcIR, opcode, type, operand) and + result = TUnaryValueNumber(funcIR, opcode, type, operand) + ) or + exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | + pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, + rightOperand) and + result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand, + rightOperand) + ) or + // The value number of a copy is just the value number of its source value. + result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue()) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll new file mode 100644 index 000000000000..3b28a05290c4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.raw.IR as IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index 26813be359cb..5e84762e7eb8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -5,3 +5,30 @@ import IRVariable import OperandTag import semmle.code.cpp.ir.implementation.EdgeKind import semmle.code.cpp.ir.implementation.MemoryAccessKind + +private newtype TIRPropertyProvider = MkIRPropertyProvider() + +/** + * Class that provides additional properties to be dumped for IR instructions and blocks when using + * the PrintIR module. Libraries that compute additional facts about IR elements can extend the + * single instance of this class to specify the additional properties computed by the library. + */ +class IRPropertyProvider extends TIRPropertyProvider { + string toString() { + result = "IRPropertyProvider" + } + + /** + * Gets the value of the property named `key` for the specified instruction. + */ + string getInstructionProperty(Instruction instruction, string key) { + none() + } + + /** + * Gets the value of the property named `key` for the specified block. + */ + string getBlockProperty(IRBlock block, string key) { + none() + } +} 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 68a3e83d2db0..a7046ce467d1 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 @@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction { result = ast.getLocation() } + /** + * Gets the `Expr` whose results is computed by this instruction, if any. + */ + final Expr getResultExpression() { + result = Construction::getInstructionResultExpression(this) + } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction { } } +/** + * An instruction that initializes the `this` pointer parameter of the enclosing function. + */ +class InitializeThisInstruction extends Instruction { + InitializeThisInstruction() { + opcode instanceof Opcode::InitializeThis + } +} + class FieldAddressInstruction extends FieldInstruction { FieldAddressInstruction() { opcode instanceof Opcode::FieldAddress diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index e745b20529a9..478e92fac5c9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -1,6 +1,18 @@ private import IR import cpp +private string getAdditionalInstructionProperty(Instruction instr, string key) { + exists(IRPropertyProvider provider | + result = provider.getInstructionProperty(instr, key) + ) +} + +private string getAdditionalBlockProperty(IRBlock block, string key) { + exists(IRPropertyProvider provider | + result = provider.getBlockProperty(block, key) + ) +} + private newtype TPrintableIRNode = TPrintableFunctionIR(FunctionIR funcIR) or TPrintableIRBlock(IRBlock block) or @@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock { result.getFunctionIR() = block.getFunctionIR() } + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalBlockProperty(block, key) + } + final IRBlock getBlock() { result = block } @@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction { final Instruction getInstruction() { result = instr } + + override string getProperty(string key) { + result = PrintableIRNode.super.getProperty(key) or + result = getAdditionalInstructionProperty(instr, key) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { 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 aad29a8c442b..4a992ffb4ce0 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 @@ -195,6 +195,10 @@ cached private module Cached { ) } + cached Expr getInstructionResultExpression(Instruction instruction) { + result = getOldInstruction(instruction).getResultExpression() + } + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind)) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll new file mode 100644 index 000000000000..e682c417ba2a --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll @@ -0,0 +1,264 @@ +private import ValueNumberInternal +import cpp +private import IR + +/** + * Provides additional information about value numbering in IR dumps. + */ +class ValueNumberPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instr, string key) { + exists(ValueNumber vn | + vn = valueNumber(instr) and + key = "valnum" and + if strictcount(vn.getAnInstruction()) > 1 then + result = vn.toString() + else + result = "unique" + ) + } +} + +newtype TValueNumber = + TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) { + variableAddressValueNumber(_, funcIR, var) + } or + TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) { + initializeParameterValueNumber(_, funcIR, var) + } or + TInitializeThisValueNumber(FunctionIR funcIR) { + initializeThisValueNumber(_, funcIR) + } or + TConstantValueNumber(FunctionIR funcIR, Type type, string value) { + constantValueNumber(_, funcIR, type, value) + } or + TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) { + fieldAddressValueNumber(_, funcIR, field, objectAddress) + } or + TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand, + ValueNumber rightOperand) { + binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand) + } or + TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize, + ValueNumber leftOperand, ValueNumber rightOperand) { + pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand) + } or + TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { + unaryValueNumber(_, funcIR, opcode, type, operand) + } or + TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { + uniqueValueNumber(instr, funcIR) + } + +/** + * The value number assigned to a particular set of instructions that produce equivalent results. + */ +class ValueNumber extends TValueNumber { + final string toString() { + result = getExampleInstruction().getResultId() + } + + final Location getLocation() { + result = getExampleInstruction().getLocation() + } + + /** + * Gets the instructions that have been assigned this value number. This will always produce at + * least one result. + */ + final Instruction getAnInstruction() { + this = valueNumber(result) + } + + /** + * Gets one of the instructions that was assigned this value number. The chosen instuction is + * deterministic but arbitrary. Intended for use only in debugging. + */ + final Instruction getExampleInstruction() { + result = min(Instruction instr | + instr = getAnInstruction() | + instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock() + ) + } +} + +class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} +class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} +class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} +class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} +class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} +class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} + +/** + * A `CopyInstruction` whose source operand's value is congruent to the definition of that source + * operand. + * For example: + * ``` + * Point p = { 1, 2 }; + * Point q = p; + * int a = p.x; + * ``` + * The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that + * definition because it accesses the exact same memory. + * The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not + * congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`. + * + * This concept should probably be exposed in the public IR API. + */ +private class CongruentCopyInstruction extends CopyInstruction { + CongruentCopyInstruction() { + exists(Instruction def | + def = this.getSourceValue() and + ( + def.getResultMemoryAccess() instanceof IndirectMemoryAccess or + not def.hasMemoryResult() + ) + ) + } +} + +/** + * Holds if this library knows how to assign a value number to the specified instruction, other than + * a `unique` value number that is never shared by multiple instructions. + */ +private predicate numberableInstruction(Instruction instr) { + instr instanceof VariableAddressInstruction or + instr instanceof InitializeParameterInstruction or + instr instanceof InitializeThisInstruction or + instr instanceof ConstantInstruction or + instr instanceof FieldAddressInstruction or + instr instanceof BinaryInstruction or + instr instanceof UnaryInstruction or + instr instanceof PointerArithmeticInstruction or + instr instanceof CongruentCopyInstruction +} + +private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR, + IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeParameterValueNumber(InitializeParameterInstruction instr, + FunctionIR funcIR, IRVariable var) { + instr.getFunctionIR() = funcIR and + instr.getVariable() = var +} + +private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR +} + +private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type, + string value) { + instr.getFunctionIR() = funcIR and + instr.getResultType() = type and + instr.getValue() = value +} + +private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR, + Field field, ValueNumber objectAddress) { + instr.getFunctionIR() = funcIR and + instr.getField() = field and + valueNumber(instr.getObjectAddress()) = objectAddress +} + +private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber leftOperand, ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof PointerArithmeticInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr, + FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getResultType() = type and + instr.getElementSize() = elementSize and + valueNumber(instr.getLeftOperand()) = leftOperand and + valueNumber(instr.getRightOperand()) = rightOperand +} + +private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode, + Type type, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + (not instr instanceof InheritanceConversionInstruction) and + (not instr instanceof FieldAddressInstruction) and + instr.getOpcode() = opcode and + instr.getResultType() = type and + valueNumber(instr.getOperand()) = operand +} + +/** + * Holds if `instr` should be assigned a unique value number because this library does not know how + * to determine if two instances of that instruction are equivalent. + */ +private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) { + instr.getFunctionIR() = funcIR and + (not instr.getResultType() instanceof VoidType) and + not numberableInstruction(instr) +} + +/** + * Gets the value number assigned to `instr`, if any. Returns at most one result. + */ +ValueNumber valueNumber(Instruction instr) { + result = nonUniqueValueNumber(instr) or + exists(FunctionIR funcIR | + uniqueValueNumber(instr, funcIR) and + result = TUniqueValueNumber(funcIR, instr) + ) +} + +/** + * Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique + * value number. + */ +private ValueNumber nonUniqueValueNumber(Instruction instr) { + exists(FunctionIR funcIR | + funcIR = instr.getFunctionIR() and + ( + exists(IRVariable var | + variableAddressValueNumber(instr, funcIR, var) and + result = TVariableAddressValueNumber(funcIR, var) + ) or + exists(IRVariable var | + initializeParameterValueNumber(instr, funcIR, var) and + result = TInitializeParameterValueNumber(funcIR, var) + ) or + ( + initializeThisValueNumber(instr, funcIR) and + result = TInitializeThisValueNumber(funcIR) + ) or + exists(Type type, string value | + constantValueNumber(instr, funcIR, type, value) and + result = TConstantValueNumber(funcIR, type, value) + ) or + exists(Field field, ValueNumber objectAddress | + fieldAddressValueNumber(instr, funcIR, field, objectAddress) and + result = TFieldAddressValueNumber(funcIR, field, objectAddress) + ) or + exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and + result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand) + ) or + exists(Opcode opcode, Type type, ValueNumber operand | + unaryValueNumber(instr, funcIR, opcode, type, operand) and + result = TUnaryValueNumber(funcIR, opcode, type, operand) + ) or + exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | + pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, + rightOperand) and + result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand, + rightOperand) + ) or + // The value number of a copy is just the value number of its source value. + result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue()) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll new file mode 100644 index 000000000000..9b4f813a10bd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected new file mode 100644 index 000000000000..479df8570b8d --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -0,0 +1,664 @@ +test.cpp: +# 1| test00(int, int) -> int +# 1| Block 0 +# 1| v0_0(void) = EnterFunction : +# 1| mu0_1(unknown) = UnmodeledDefinition : +# 1| valnum = unique +# 1| r0_2(glval) = VariableAddress[p0] : +# 1| valnum = r0_2 +# 1| m0_3(int) = InitializeParameter[p0] : r0_2 +# 1| valnum = m0_3 +# 1| r0_4(glval) = VariableAddress[p1] : +# 1| valnum = r0_4 +# 1| m0_5(int) = InitializeParameter[p1] : r0_4 +# 1| valnum = m0_5 +# 2| r0_6(glval) = VariableAddress[x] : +# 2| valnum = r0_6 +# 2| m0_7(int) = Uninitialized : r0_6 +# 2| valnum = unique +# 2| r0_8(glval) = VariableAddress[y] : +# 2| valnum = r0_8 +# 2| m0_9(int) = Uninitialized : r0_8 +# 2| valnum = unique +# 3| r0_10(glval) = VariableAddress[b] : +# 3| valnum = unique +# 3| m0_11(unsigned char) = Uninitialized : r0_10 +# 3| valnum = unique +# 5| r0_12(glval) = VariableAddress[p0] : +# 5| valnum = r0_2 +# 5| r0_13(int) = Load : r0_12, m0_3 +# 5| valnum = m0_3 +# 5| r0_14(glval) = VariableAddress[p1] : +# 5| valnum = r0_4 +# 5| r0_15(int) = Load : r0_14, m0_5 +# 5| valnum = m0_5 +# 5| r0_16(int) = Add : r0_13, r0_15 +# 5| valnum = r0_16 +# 5| r0_17(glval) = VariableAddress[x] : +# 5| valnum = r0_6 +# 5| m0_18(int) = Store : r0_17, r0_16 +# 5| valnum = r0_16 +# 6| r0_19(glval) = VariableAddress[p0] : +# 6| valnum = r0_2 +# 6| r0_20(int) = Load : r0_19, m0_3 +# 6| valnum = m0_3 +# 6| r0_21(glval) = VariableAddress[p1] : +# 6| valnum = r0_4 +# 6| r0_22(int) = Load : r0_21, m0_5 +# 6| valnum = m0_5 +# 6| r0_23(int) = Add : r0_20, r0_22 +# 6| valnum = r0_16 +# 6| r0_24(glval) = VariableAddress[x] : +# 6| valnum = r0_6 +# 6| m0_25(int) = Store : r0_24, r0_23 +# 6| valnum = r0_16 +# 7| r0_26(glval) = VariableAddress[x] : +# 7| valnum = r0_6 +# 7| r0_27(int) = Load : r0_26, m0_25 +# 7| valnum = r0_16 +# 7| r0_28(glval) = VariableAddress[y] : +# 7| valnum = r0_8 +# 7| m0_29(int) = Store : r0_28, r0_27 +# 7| valnum = r0_16 +# 8| v0_30(void) = NoOp : +# 1| r0_31(glval) = VariableAddress[#return] : +# 1| valnum = unique +# 1| v0_32(void) = ReturnValue : r0_31 +# 1| v0_33(void) = UnmodeledUse : mu* +# 1| v0_34(void) = ExitFunction : + +# 53| 1644 +# 53| valnum = unique + +# 56| 1646 +# 56| valnum = unique + +# 88| 1755 +# 88| valnum = unique + +# 12| test01(int, int) -> int +# 12| Block 0 +# 12| v0_0(void) = EnterFunction : +# 12| mu0_1(unknown) = UnmodeledDefinition : +# 12| valnum = unique +# 12| r0_2(glval) = VariableAddress[p0] : +# 12| valnum = r0_2 +# 12| m0_3(int) = InitializeParameter[p0] : r0_2 +# 12| valnum = m0_3 +# 12| r0_4(glval) = VariableAddress[p1] : +# 12| valnum = r0_4 +# 12| m0_5(int) = InitializeParameter[p1] : r0_4 +# 12| valnum = m0_5 +# 13| r0_6(glval) = VariableAddress[x] : +# 13| valnum = r0_6 +# 13| m0_7(int) = Uninitialized : r0_6 +# 13| valnum = unique +# 13| r0_8(glval) = VariableAddress[y] : +# 13| valnum = r0_8 +# 13| m0_9(int) = Uninitialized : r0_8 +# 13| valnum = unique +# 14| r0_10(glval) = VariableAddress[b] : +# 14| valnum = unique +# 14| m0_11(unsigned char) = Uninitialized : r0_10 +# 14| valnum = unique +# 16| r0_12(glval) = VariableAddress[p0] : +# 16| valnum = r0_2 +# 16| r0_13(int) = Load : r0_12, m0_3 +# 16| valnum = m0_3 +# 16| r0_14(glval) = VariableAddress[p1] : +# 16| valnum = r0_4 +# 16| r0_15(int) = Load : r0_14, m0_5 +# 16| valnum = m0_5 +# 16| r0_16(int) = Add : r0_13, r0_15 +# 16| valnum = r0_16 +# 16| r0_17(glval) = VariableAddress[global01] : +# 16| valnum = r0_17 +# 16| r0_18(int) = Load : r0_17, mu0_1 +# 16| valnum = unique +# 16| r0_19(int) = Add : r0_16, r0_18 +# 16| valnum = r0_19 +# 16| r0_20(glval) = VariableAddress[x] : +# 16| valnum = r0_6 +# 16| m0_21(int) = Store : r0_20, r0_19 +# 16| valnum = r0_19 +# 17| r0_22(glval) = VariableAddress[p0] : +# 17| valnum = r0_2 +# 17| r0_23(int) = Load : r0_22, m0_3 +# 17| valnum = m0_3 +# 17| r0_24(glval) = VariableAddress[p1] : +# 17| valnum = r0_4 +# 17| r0_25(int) = Load : r0_24, m0_5 +# 17| valnum = m0_5 +# 17| r0_26(int) = Add : r0_23, r0_25 +# 17| valnum = r0_16 +# 17| r0_27(glval) = VariableAddress[global01] : +# 17| valnum = r0_17 +# 17| r0_28(int) = Load : r0_27, mu0_1 +# 17| valnum = unique +# 17| r0_29(int) = Add : r0_26, r0_28 +# 17| valnum = r0_29 +# 17| r0_30(glval) = VariableAddress[x] : +# 17| valnum = r0_6 +# 17| m0_31(int) = Store : r0_30, r0_29 +# 17| valnum = r0_29 +# 18| r0_32(glval) = VariableAddress[x] : +# 18| valnum = r0_6 +# 18| r0_33(int) = Load : r0_32, m0_31 +# 18| valnum = r0_29 +# 18| r0_34(glval) = VariableAddress[y] : +# 18| valnum = r0_8 +# 18| m0_35(int) = Store : r0_34, r0_33 +# 18| valnum = r0_29 +# 19| v0_36(void) = NoOp : +# 12| r0_37(glval) = VariableAddress[#return] : +# 12| valnum = unique +# 12| v0_38(void) = ReturnValue : r0_37 +# 12| v0_39(void) = UnmodeledUse : mu* +# 12| v0_40(void) = ExitFunction : + +# 25| test02(int, int) -> int +# 25| Block 0 +# 25| v0_0(void) = EnterFunction : +# 25| mu0_1(unknown) = UnmodeledDefinition : +# 25| valnum = unique +# 25| r0_2(glval) = VariableAddress[p0] : +# 25| valnum = r0_2 +# 25| m0_3(int) = InitializeParameter[p0] : r0_2 +# 25| valnum = m0_3 +# 25| r0_4(glval) = VariableAddress[p1] : +# 25| valnum = r0_4 +# 25| m0_5(int) = InitializeParameter[p1] : r0_4 +# 25| valnum = m0_5 +# 26| r0_6(glval) = VariableAddress[x] : +# 26| valnum = r0_6 +# 26| m0_7(int) = Uninitialized : r0_6 +# 26| valnum = unique +# 26| r0_8(glval) = VariableAddress[y] : +# 26| valnum = r0_8 +# 26| m0_9(int) = Uninitialized : r0_8 +# 26| valnum = unique +# 27| r0_10(glval) = VariableAddress[b] : +# 27| valnum = unique +# 27| m0_11(unsigned char) = Uninitialized : r0_10 +# 27| valnum = unique +# 29| r0_12(glval) = VariableAddress[p0] : +# 29| valnum = r0_2 +# 29| r0_13(int) = Load : r0_12, m0_3 +# 29| valnum = m0_3 +# 29| r0_14(glval) = VariableAddress[p1] : +# 29| valnum = r0_4 +# 29| r0_15(int) = Load : r0_14, m0_5 +# 29| valnum = m0_5 +# 29| r0_16(int) = Add : r0_13, r0_15 +# 29| valnum = r0_16 +# 29| r0_17(glval) = VariableAddress[global02] : +# 29| valnum = r0_17 +# 29| r0_18(int) = Load : r0_17, mu0_1 +# 29| valnum = unique +# 29| r0_19(int) = Add : r0_16, r0_18 +# 29| valnum = r0_19 +# 29| r0_20(glval) = VariableAddress[x] : +# 29| valnum = r0_6 +# 29| m0_21(int) = Store : r0_20, r0_19 +# 29| valnum = r0_19 +# 30| r0_22(glval) = FunctionAddress[change_global02] : +# 30| valnum = unique +# 30| v0_23(void) = Call : r0_22 +# 31| r0_24(glval) = VariableAddress[p0] : +# 31| valnum = r0_2 +# 31| r0_25(int) = Load : r0_24, m0_3 +# 31| valnum = m0_3 +# 31| r0_26(glval) = VariableAddress[p1] : +# 31| valnum = r0_4 +# 31| r0_27(int) = Load : r0_26, m0_5 +# 31| valnum = m0_5 +# 31| r0_28(int) = Add : r0_25, r0_27 +# 31| valnum = r0_16 +# 31| r0_29(glval) = VariableAddress[global02] : +# 31| valnum = r0_17 +# 31| r0_30(int) = Load : r0_29, mu0_1 +# 31| valnum = unique +# 31| r0_31(int) = Add : r0_28, r0_30 +# 31| valnum = r0_31 +# 31| r0_32(glval) = VariableAddress[x] : +# 31| valnum = r0_6 +# 31| m0_33(int) = Store : r0_32, r0_31 +# 31| valnum = r0_31 +# 32| r0_34(glval) = VariableAddress[x] : +# 32| valnum = r0_6 +# 32| r0_35(int) = Load : r0_34, m0_33 +# 32| valnum = r0_31 +# 32| r0_36(glval) = VariableAddress[y] : +# 32| valnum = r0_8 +# 32| m0_37(int) = Store : r0_36, r0_35 +# 32| valnum = r0_31 +# 33| v0_38(void) = NoOp : +# 25| r0_39(glval) = VariableAddress[#return] : +# 25| valnum = unique +# 25| v0_40(void) = ReturnValue : r0_39 +# 25| v0_41(void) = UnmodeledUse : mu* +# 25| v0_42(void) = ExitFunction : + +# 39| test03(int, int, int *) -> int +# 39| Block 0 +# 39| v0_0(void) = EnterFunction : +# 39| mu0_1(unknown) = UnmodeledDefinition : +# 39| valnum = unique +# 39| r0_2(glval) = VariableAddress[p0] : +# 39| valnum = r0_2 +# 39| m0_3(int) = InitializeParameter[p0] : r0_2 +# 39| valnum = m0_3 +# 39| r0_4(glval) = VariableAddress[p1] : +# 39| valnum = r0_4 +# 39| m0_5(int) = InitializeParameter[p1] : r0_4 +# 39| valnum = m0_5 +# 39| r0_6(glval) = VariableAddress[p2] : +# 39| valnum = r0_6 +# 39| m0_7(int *) = InitializeParameter[p2] : r0_6 +# 39| valnum = m0_7 +# 40| r0_8(glval) = VariableAddress[x] : +# 40| valnum = r0_8 +# 40| m0_9(int) = Uninitialized : r0_8 +# 40| valnum = unique +# 40| r0_10(glval) = VariableAddress[y] : +# 40| valnum = r0_10 +# 40| m0_11(int) = Uninitialized : r0_10 +# 40| valnum = unique +# 41| r0_12(glval) = VariableAddress[b] : +# 41| valnum = unique +# 41| m0_13(unsigned char) = Uninitialized : r0_12 +# 41| valnum = unique +# 43| r0_14(glval) = VariableAddress[p0] : +# 43| valnum = r0_2 +# 43| r0_15(int) = Load : r0_14, m0_3 +# 43| valnum = m0_3 +# 43| r0_16(glval) = VariableAddress[p1] : +# 43| valnum = r0_4 +# 43| r0_17(int) = Load : r0_16, m0_5 +# 43| valnum = m0_5 +# 43| r0_18(int) = Add : r0_15, r0_17 +# 43| valnum = r0_18 +# 43| r0_19(glval) = VariableAddress[global03] : +# 43| valnum = r0_19 +# 43| r0_20(int) = Load : r0_19, mu0_1 +# 43| valnum = unique +# 43| r0_21(int) = Add : r0_18, r0_20 +# 43| valnum = r0_21 +# 43| r0_22(glval) = VariableAddress[x] : +# 43| valnum = r0_8 +# 43| m0_23(int) = Store : r0_22, r0_21 +# 43| valnum = r0_21 +# 44| r0_24(int) = Constant[0] : +# 44| valnum = r0_24 +# 44| r0_25(glval) = VariableAddress[p2] : +# 44| valnum = r0_6 +# 44| r0_26(int *) = Load : r0_25, m0_7 +# 44| valnum = m0_7 +# 44| mu0_27(int) = Store : r0_26, r0_24 +# 44| valnum = r0_24 +# 45| r0_28(glval) = VariableAddress[p0] : +# 45| valnum = r0_2 +# 45| r0_29(int) = Load : r0_28, m0_3 +# 45| valnum = m0_3 +# 45| r0_30(glval) = VariableAddress[p1] : +# 45| valnum = r0_4 +# 45| r0_31(int) = Load : r0_30, m0_5 +# 45| valnum = m0_5 +# 45| r0_32(int) = Add : r0_29, r0_31 +# 45| valnum = r0_18 +# 45| r0_33(glval) = VariableAddress[global03] : +# 45| valnum = r0_19 +# 45| r0_34(int) = Load : r0_33, mu0_1 +# 45| valnum = unique +# 45| r0_35(int) = Add : r0_32, r0_34 +# 45| valnum = r0_35 +# 45| r0_36(glval) = VariableAddress[x] : +# 45| valnum = r0_8 +# 45| m0_37(int) = Store : r0_36, r0_35 +# 45| valnum = r0_35 +# 46| r0_38(glval) = VariableAddress[x] : +# 46| valnum = r0_8 +# 46| r0_39(int) = Load : r0_38, m0_37 +# 46| valnum = r0_35 +# 46| r0_40(glval) = VariableAddress[y] : +# 46| valnum = r0_10 +# 46| m0_41(int) = Store : r0_40, r0_39 +# 46| valnum = r0_35 +# 47| v0_42(void) = NoOp : +# 39| r0_43(glval) = VariableAddress[#return] : +# 39| valnum = unique +# 39| v0_44(void) = ReturnValue : r0_43 +# 39| v0_45(void) = UnmodeledUse : mu* +# 39| v0_46(void) = ExitFunction : + +# 49| my_strspn(const char *, const char *) -> unsigned int +# 49| Block 0 +# 49| v0_0(void) = EnterFunction : +# 49| mu0_1(unknown) = UnmodeledDefinition : +# 49| valnum = unique +# 49| r0_2(glval) = VariableAddress[str] : +# 49| valnum = r0_2 +# 49| m0_3(char *) = InitializeParameter[str] : r0_2 +# 49| valnum = m0_3 +# 49| r0_4(glval) = VariableAddress[chars] : +# 49| valnum = r0_4 +# 49| m0_5(char *) = InitializeParameter[chars] : r0_4 +# 49| valnum = m0_5 +# 50| r0_6(glval) = VariableAddress[ptr] : +# 50| valnum = r0_6 +# 50| m0_7(char *) = Uninitialized : r0_6 +# 50| valnum = unique +# 51| r0_8(glval) = VariableAddress[result] : +# 51| valnum = r0_8 +# 51| r0_9(unsigned int) = Constant[0] : +# 51| valnum = r0_9 +# 51| m0_10(unsigned int) = Store : r0_8, r0_9 +# 51| valnum = r0_9 +#-----| Goto -> Block 1 + +# 53| Block 1 +# 53| m1_0(unsigned int) = Phi : from 0:m0_10, from 8:m8_4 +# 53| valnum = unique +# 53| r1_1(glval) = VariableAddress[str] : +# 53| valnum = r0_2 +# 53| r1_2(char *) = Load : r1_1, m0_3 +# 53| valnum = m0_3 +# 53| r1_3(char) = Load : r1_2, mu0_1 +# 53| valnum = unique +# 53| r1_4(int) = Convert : r1_3 +# 53| valnum = unique +# 53| r1_5(int) = Constant[0] : +# 53| valnum = r1_5 +# 53| r1_6(bool) = CompareNE : r1_4, r1_5 +# 53| valnum = unique +# 53| v1_7(void) = ConditionalBranch : r1_6 +#-----| False -> Block 9 +#-----| True -> Block 2 + +# 55| Block 2 +# 55| r2_0(glval) = VariableAddress[chars] : +# 55| valnum = r0_4 +# 55| r2_1(char *) = Load : r2_0, m0_5 +# 55| valnum = m0_5 +# 55| r2_2(glval) = VariableAddress[ptr] : +# 55| valnum = r0_6 +# 55| m2_3(char *) = Store : r2_2, r2_1 +# 55| valnum = m0_5 +#-----| Goto -> Block 3 + +# 56| Block 3 +# 56| m3_0(char *) = Phi : from 2:m2_3, from 5:m5_4 +# 56| valnum = unique +# 56| r3_1(glval) = VariableAddress[ptr] : +# 56| valnum = r0_6 +# 56| r3_2(char *) = Load : r3_1, m3_0 +# 56| valnum = unique +# 56| r3_3(char) = Load : r3_2, mu0_1 +# 56| valnum = unique +# 56| r3_4(int) = Convert : r3_3 +# 56| valnum = unique +# 56| r3_5(glval) = VariableAddress[str] : +# 56| valnum = r0_2 +# 56| r3_6(char *) = Load : r3_5, m0_3 +# 56| valnum = m0_3 +# 56| r3_7(char) = Load : r3_6, mu0_1 +# 56| valnum = unique +# 56| r3_8(int) = Convert : r3_7 +# 56| valnum = unique +# 56| r3_9(bool) = CompareNE : r3_4, r3_8 +# 56| valnum = unique +# 56| v3_10(void) = ConditionalBranch : r3_9 +#-----| False -> Block 6 +#-----| True -> Block 4 + +# 56| Block 4 +# 56| r4_0(glval) = VariableAddress[ptr] : +# 56| valnum = r0_6 +# 56| r4_1(char *) = Load : r4_0, m3_0 +# 56| valnum = unique +# 56| r4_2(char) = Load : r4_1, mu0_1 +# 56| valnum = unique +# 56| r4_3(int) = Convert : r4_2 +# 56| valnum = unique +# 56| r4_4(int) = Constant[0] : +# 56| valnum = r1_5 +# 56| r4_5(bool) = CompareNE : r4_3, r4_4 +# 56| valnum = unique +# 56| v4_6(void) = ConditionalBranch : r4_5 +#-----| False -> Block 6 +#-----| True -> Block 5 + +# 56| Block 5 +# 56| r5_0(glval) = VariableAddress[ptr] : +# 56| valnum = r0_6 +# 56| r5_1(char *) = Load : r5_0, m3_0 +# 56| valnum = unique +# 56| r5_2(int) = Constant[1] : +# 56| valnum = unique +# 56| r5_3(char *) = PointerAdd[1] : r5_1, r5_2 +# 56| valnum = r5_3 +# 56| m5_4(char *) = Store : r5_0, r5_3 +# 56| valnum = r5_3 +#-----| Goto -> Block 3 + +# 59| Block 6 +# 59| r6_0(glval) = VariableAddress[ptr] : +# 59| valnum = r0_6 +# 59| r6_1(char *) = Load : r6_0, m3_0 +# 59| valnum = unique +# 59| r6_2(char) = Load : r6_1, mu0_1 +# 59| valnum = unique +# 59| r6_3(int) = Convert : r6_2 +# 59| valnum = unique +# 59| r6_4(int) = Constant[0] : +# 59| valnum = r1_5 +# 59| r6_5(bool) = CompareEQ : r6_3, r6_4 +# 59| valnum = unique +# 59| v6_6(void) = ConditionalBranch : r6_5 +#-----| False -> Block 8 +#-----| True -> Block 7 + +# 60| Block 7 +# 60| v7_0(void) = NoOp : +#-----| Goto -> Block 9 + +# 62| Block 8 +# 62| r8_0(glval) = VariableAddress[result] : +# 62| valnum = r0_8 +# 62| r8_1(unsigned int) = Load : r8_0, m1_0 +# 62| valnum = unique +# 62| r8_2(unsigned int) = Constant[1] : +# 62| valnum = unique +# 62| r8_3(unsigned int) = Add : r8_1, r8_2 +# 62| valnum = r8_3 +# 62| m8_4(unsigned int) = Store : r8_0, r8_3 +# 62| valnum = r8_3 +#-----| Goto -> Block 1 + +# 63| Block 9 +# 63| v9_0(void) = NoOp : +# 65| r9_1(glval) = VariableAddress[#return] : +# 65| valnum = r9_1 +# 65| r9_2(glval) = VariableAddress[result] : +# 65| valnum = r0_8 +# 65| r9_3(unsigned int) = Load : r9_2, m1_0 +# 65| valnum = r9_3 +# 65| m9_4(unsigned int) = Store : r9_1, r9_3 +# 65| valnum = r9_3 +# 49| r9_5(glval) = VariableAddress[#return] : +# 49| valnum = r9_1 +# 49| v9_6(void) = ReturnValue : r9_5, m9_4 +# 49| v9_7(void) = UnmodeledUse : mu* +# 49| v9_8(void) = ExitFunction : + +# 75| test04(two_values *) -> void +# 75| Block 0 +# 75| v0_0(void) = EnterFunction : +# 75| mu0_1(unknown) = UnmodeledDefinition : +# 75| valnum = unique +# 75| r0_2(glval) = VariableAddress[vals] : +# 75| valnum = r0_2 +# 75| m0_3(two_values *) = InitializeParameter[vals] : r0_2 +# 75| valnum = m0_3 +# 77| r0_4(glval) = VariableAddress[v] : +# 77| valnum = r0_4 +# 77| r0_5(glval) = FunctionAddress[getAValue] : +# 77| valnum = unique +# 77| r0_6(int) = Call : r0_5 +# 77| valnum = unique +# 77| r0_7(signed short) = Convert : r0_6 +# 77| valnum = r0_7 +# 77| m0_8(signed short) = Store : r0_4, r0_7 +# 77| valnum = r0_7 +# 79| r0_9(glval) = VariableAddress[v] : +# 79| valnum = r0_4 +# 79| r0_10(signed short) = Load : r0_9, m0_8 +# 79| valnum = r0_7 +# 79| r0_11(int) = Convert : r0_10 +# 79| valnum = unique +# 79| r0_12(glval) = VariableAddress[vals] : +# 79| valnum = r0_2 +# 79| r0_13(two_values *) = Load : r0_12, m0_3 +# 79| valnum = m0_3 +# 79| r0_14(glval) = FieldAddress[val1] : r0_13 +# 79| valnum = unique +# 79| r0_15(signed short) = Load : r0_14, mu0_1 +# 79| valnum = unique +# 79| r0_16(int) = Convert : r0_15 +# 79| valnum = unique +# 79| r0_17(glval) = VariableAddress[vals] : +# 79| valnum = r0_2 +# 79| r0_18(two_values *) = Load : r0_17, m0_3 +# 79| valnum = m0_3 +# 79| r0_19(glval) = FieldAddress[val2] : r0_18 +# 79| valnum = unique +# 79| r0_20(signed short) = Load : r0_19, mu0_1 +# 79| valnum = unique +# 79| r0_21(int) = Convert : r0_20 +# 79| valnum = unique +# 79| r0_22(int) = Add : r0_16, r0_21 +# 79| valnum = unique +# 79| r0_23(bool) = CompareLT : r0_11, r0_22 +# 79| valnum = unique +# 79| v0_24(void) = ConditionalBranch : r0_23 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 80| Block 1 +# 80| r1_0(glval) = FunctionAddress[getAValue] : +# 80| valnum = unique +# 80| r1_1(int) = Call : r1_0 +# 80| valnum = unique +# 80| r1_2(signed short) = Convert : r1_1 +# 80| valnum = r1_2 +# 80| r1_3(glval) = VariableAddress[v] : +# 80| valnum = r0_4 +# 80| m1_4(signed short) = Store : r1_3, r1_2 +# 80| valnum = r1_2 +#-----| Goto -> Block 2 + +# 82| Block 2 +# 82| v2_0(void) = NoOp : +# 75| v2_1(void) = ReturnVoid : +# 75| v2_2(void) = UnmodeledUse : mu* +# 75| v2_3(void) = ExitFunction : + +# 84| test05(int, int, void *) -> void +# 84| Block 0 +# 84| v0_0(void) = EnterFunction : +# 84| mu0_1(unknown) = UnmodeledDefinition : +# 84| valnum = unique +# 84| r0_2(glval) = VariableAddress[x] : +# 84| valnum = r0_2 +# 84| m0_3(int) = InitializeParameter[x] : r0_2 +# 84| valnum = m0_3 +# 84| r0_4(glval) = VariableAddress[y] : +# 84| valnum = r0_4 +# 84| m0_5(int) = InitializeParameter[y] : r0_4 +# 84| valnum = m0_5 +# 84| r0_6(glval) = VariableAddress[p] : +# 84| valnum = r0_6 +# 84| m0_7(void *) = InitializeParameter[p] : r0_6 +# 84| valnum = m0_7 +# 86| r0_8(glval) = VariableAddress[v] : +# 86| valnum = r0_8 +# 86| m0_9(int) = Uninitialized : r0_8 +# 86| valnum = unique +# 88| r0_10(glval) = VariableAddress[p] : +# 88| valnum = r0_6 +# 88| r0_11(void *) = Load : r0_10, m0_7 +# 88| valnum = m0_7 +# 88| r0_12(void *) = Constant[0] : +# 88| valnum = unique +# 88| r0_13(bool) = CompareNE : r0_11, r0_12 +# 88| valnum = unique +# 88| v0_14(void) = ConditionalBranch : r0_13 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 88| Block 1 +# 88| r1_0(glval) = VariableAddress[x] : +# 88| valnum = r0_2 +# 88| r1_1(int) = Load : r1_0, m0_3 +# 88| valnum = m0_3 +# 88| r1_2(glval) = VariableAddress[#temp88:7] : +# 88| valnum = r1_2 +# 88| m1_3(int) = Store : r1_2, r1_1 +# 88| valnum = m0_3 +#-----| Goto -> Block 3 + +# 88| Block 2 +# 88| r2_0(glval) = VariableAddress[y] : +# 88| valnum = r0_4 +# 88| r2_1(int) = Load : r2_0, m0_5 +# 88| valnum = m0_5 +# 88| r2_2(glval) = VariableAddress[#temp88:7] : +# 88| valnum = r1_2 +# 88| m2_3(int) = Store : r2_2, r2_1 +# 88| valnum = m0_5 +#-----| Goto -> Block 3 + +# 88| Block 3 +# 88| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3 +# 88| valnum = unique +# 88| r3_1(glval) = VariableAddress[#temp88:7] : +# 88| valnum = r1_2 +# 88| r3_2(int) = Load : r3_1, m3_0 +# 88| valnum = r3_2 +# 88| r3_3(glval) = VariableAddress[v] : +# 88| valnum = r0_8 +# 88| m3_4(int) = Store : r3_3, r3_2 +# 88| valnum = r3_2 +# 89| v3_5(void) = NoOp : +# 84| v3_6(void) = ReturnVoid : +# 84| v3_7(void) = UnmodeledUse : mu* +# 84| v3_8(void) = ExitFunction : + +# 91| regression_test00() -> int +# 91| Block 0 +# 91| v0_0(void) = EnterFunction : +# 91| mu0_1(unknown) = UnmodeledDefinition : +# 91| valnum = unique +# 92| r0_2(glval) = VariableAddress[x] : +# 92| valnum = r0_2 +# 92| r0_3(int) = Constant[10] : +# 92| valnum = r0_3 +# 92| r0_4(glval) = VariableAddress[x] : +# 92| valnum = r0_2 +# 92| m0_5(int) = Store : r0_4, r0_3 +# 92| valnum = r0_3 +# 92| m0_6(int) = Store : r0_2, r0_3 +# 92| valnum = r0_3 +# 93| r0_7(glval) = VariableAddress[#return] : +# 93| valnum = r0_7 +# 93| r0_8(glval) = VariableAddress[x] : +# 93| valnum = r0_2 +# 93| r0_9(int) = Load : r0_8, m0_6 +# 93| valnum = r0_3 +# 93| m0_10(int) = Store : r0_7, r0_9 +# 93| valnum = r0_3 +# 91| r0_11(glval) = VariableAddress[#return] : +# 91| valnum = r0_7 +# 91| v0_12(void) = ReturnValue : r0_11, m0_10 +# 91| v0_13(void) = UnmodeledUse : mu* +# 91| v0_14(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql new file mode 100644 index 000000000000..2b9521af1ae1 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql @@ -0,0 +1,6 @@ +/** + * @kind graph + */ +import semmle.code.cpp.ir.PrintIR +import semmle.code.cpp.ir.IR +import semmle.code.cpp.ir.implementation.aliased_ssa.internal.gvn.ValueNumber From 43f0289f0f84ff49ac0f11987099b23b40f9de6c Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Tue, 18 Sep 2018 11:28:09 -0700 Subject: [PATCH 065/110] C++: Remove Phi instructions from previous IR generations It turns out that when building aliased SSA IR, we were still keeping around the Phi instructions from unaliased SSA IR. These leftover instructions didn't show up in dumps because they were not assigned to a block. However, when dumping additional instruction properties, they would show up as a top-level node in the dump, without a label. --- .../cpp/ir/implementation/aliased_ssa/Instruction.qll | 8 ++++++++ .../aliased_ssa/internal/SSAConstruction.qll | 4 +++- .../code/cpp/ir/implementation/raw/Instruction.qll | 8 ++++++++ .../cpp/ir/implementation/unaliased_ssa/Instruction.qll | 8 ++++++++ .../unaliased_ssa/internal/SSAConstruction.qll | 4 +++- .../test/library-tests/ir/ir/aliased_ssa_sanity.expected | 1 + cpp/ql/test/library-tests/ir/ir/raw_sanity.expected | 1 + .../library-tests/ir/ir/unaliased_ssa_sanity.expected | 1 + .../valuenumbering/GlobalValueNumbering/ir_gvn.expected | 9 --------- 9 files changed, 33 insertions(+), 11 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 a7046ce467d1..e63fd979def7 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 @@ -107,6 +107,14 @@ module InstructionSanity { operand = op.getOperand(tag) and operand.getFunctionIR() != op.getFunctionIR() } + + /** + * Holds if instruction `instr` is not in exactly one block. + */ + query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { + blockCount = count(instr.getBlock()) and + blockCount != 1 + } } /** 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 4a992ffb4ce0..f6e2034296d9 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 @@ -30,7 +30,9 @@ cached private module Cached { } cached newtype TInstructionTag = - WrappedInstructionTag(OldIR::Instruction oldInstruction) or + WrappedInstructionTag(OldIR::Instruction oldInstruction) { + not oldInstruction instanceof OldIR::PhiInstruction + } or PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { hasPhiNode(vvar, block) } 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 a7046ce467d1..e63fd979def7 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 @@ -107,6 +107,14 @@ module InstructionSanity { operand = op.getOperand(tag) and operand.getFunctionIR() != op.getFunctionIR() } + + /** + * Holds if instruction `instr` is not in exactly one block. + */ + query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { + blockCount = count(instr.getBlock()) and + blockCount != 1 + } } /** 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 a7046ce467d1..e63fd979def7 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 @@ -107,6 +107,14 @@ module InstructionSanity { operand = op.getOperand(tag) and operand.getFunctionIR() != op.getFunctionIR() } + + /** + * Holds if instruction `instr` is not in exactly one block. + */ + query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { + blockCount = count(instr.getBlock()) and + blockCount != 1 + } } /** 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 4a992ffb4ce0..f6e2034296d9 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 @@ -30,7 +30,9 @@ cached private module Cached { } cached newtype TInstructionTag = - WrappedInstructionTag(OldIR::Instruction oldInstruction) or + WrappedInstructionTag(OldIR::Instruction oldInstruction) { + not oldInstruction instanceof OldIR::PhiInstruction + } or PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) { hasPhiNode(vvar, block) } diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected index 001551870faf..95989fc88090 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected @@ -5,3 +5,4 @@ missingPhiOperand instructionWithoutSuccessor unnecessaryPhiInstruction operandAcrossFunctions +instructionWithoutUniqueBlock diff --git a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected index 001551870faf..95989fc88090 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected @@ -5,3 +5,4 @@ missingPhiOperand instructionWithoutSuccessor unnecessaryPhiInstruction operandAcrossFunctions +instructionWithoutUniqueBlock diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected index 001551870faf..95989fc88090 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected @@ -5,3 +5,4 @@ missingPhiOperand instructionWithoutSuccessor unnecessaryPhiInstruction operandAcrossFunctions +instructionWithoutUniqueBlock diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 479df8570b8d..35b507a61f17 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -67,15 +67,6 @@ test.cpp: # 1| v0_33(void) = UnmodeledUse : mu* # 1| v0_34(void) = ExitFunction : -# 53| 1644 -# 53| valnum = unique - -# 56| 1646 -# 56| valnum = unique - -# 88| 1755 -# 88| valnum = unique - # 12| test01(int, int) -> int # 12| Block 0 # 12| v0_0(void) = EnterFunction : From 2cedc817744f3d061116cc0dd89a61cf4fc47ebb Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 17 Sep 2018 12:18:05 +0200 Subject: [PATCH 066/110] JS: polish js/enabling-electron-renderer-node-integration meta info --- change-notes/1.19/analysis-javascript.md | 1 + javascript/config/suites/javascript/security | 1 + .../Electron/EnablingNodeIntegration.qhelp | 51 +++++++++++-------- .../src/Electron/EnablingNodeIntegration.ql | 4 +- .../examples/DefaultNodeIntegration.js | 2 - .../examples/EnablingNodeIntegration.js | 33 +++++------- 6 files changed, 49 insertions(+), 43 deletions(-) delete mode 100644 javascript/ql/src/Electron/examples/DefaultNodeIntegration.js diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 4d23f695d9e0..4415af95d30c 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -12,6 +12,7 @@ | **Query** | **Tags** | **Purpose** | |-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). Results are not shown on LGTM by default. | | Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/security b/javascript/config/suites/javascript/security index e55f89019f68..2f0fd0dabc0f 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -1,4 +1,5 @@ + semmlecode-javascript-queries/DOM/TargetBlank.ql: /Security/CWE/CWE-200 ++ semmlecode-javascript-queries/Electron/EnablingNodeIntegration.ql: /Security/CWE/CWE-094 + semmlecode-javascript-queries/Security/CWE-022/TaintedPath.ql: /Security/CWE/CWE-022 + semmlecode-javascript-queries/Security/CWE-078/CommandInjection.ql: /Security/CWE/CWE-078 + semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079 diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp index c6a9af938d70..34efea3b347e 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -5,39 +5,48 @@

    - Enabling Node.js integration in web content renderers (BrowserWindow, BrowserView and webview) could result in - remote native code execution attacks when rendering malicious JavaScript code from untrusted remote web site or - code that is injected via a cross site scripting vulnerability into a trusted remote web site. Note that - the nodeIntegration property is enabled by default in Electron and needs to be set to false explicitly. -

    + + Enabling Node.js integration in Electron web content renderers + (BrowserWindow, BrowserView and + webview) can result in remote native code execution + attacks. + + The attack is realized when the renderer uses content from an + untrusted remote web site or a trusted site with a cross site + scripting vulnerability. + +

    - Node.js integration should be disabled when loading remote web sites. If not possible, always set nodeIntegration property - to 'false' before loading remote web sites and only enable it for whitelisted sites. -

    -
    - - -

    - The following example shows insecure use of BrowserWindow with regards to nodeIntegration - property: + + Node.js integration should be disabled when loading remote web + sites. Always set nodeIntegration preference + to false before loading remote web sites, and only enable + it for whitelisted sites. +

    -

    - This is problematic, because default value of nodeIntegration is 'true'. + + Note that the nodeIntegration property is enabled + by default in Electron and needs to be set to false + explicitly. +

    - -
    + - +

    - The following example shows insecure and secure uses of BrowserWindow and BrowserView when - loading untrusted web sites: + + The following examples shows insecure and secure uses of + BrowserWindow and BrowserView when loading + remote web sites: +

    +
    diff --git a/javascript/ql/src/Electron/EnablingNodeIntegration.ql b/javascript/ql/src/Electron/EnablingNodeIntegration.ql index f38d854c1dc9..d8e764012a20 100644 --- a/javascript/ql/src/Electron/EnablingNodeIntegration.ql +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.ql @@ -1,11 +1,13 @@ /** - * @name Enabling `nodeIntegration` or `nodeIntegrationInWorker` for Electron web content + * @name Enabling Node.js integration for Electron web content renderers * @description Enabling `nodeIntegration` or `nodeIntegrationInWorker` can expose the application to remote code execution. * @kind problem * @problem.severity warning + * @precision low * @id js/enabling-electron-renderer-node-integration * @tags security * frameworks/electron + * external/cwe/cwe-094 */ import javascript diff --git a/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js b/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js deleted file mode 100644 index 781d31f59f6f..000000000000 --- a/javascript/ql/src/Electron/examples/DefaultNodeIntegration.js +++ /dev/null @@ -1,2 +0,0 @@ -const win = new BrowserWindow(); -win.loadURL("https://untrusted-site.com"); \ No newline at end of file diff --git a/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js b/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js index 7a6312a807b5..87510f062650 100644 --- a/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js +++ b/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js @@ -1,26 +1,21 @@ -//BAD -win_1 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); -win_1.loadURL("https://untrusted-site.com"); +//BAD: `nodeIntegration` enabled by default +var win_1 = new BrowserWindow(); +win_1.loadURL(remote_site); -//GOOD -win_2 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: false}}); -win_2.loadURL("https://untrusted-site.com"); +//BAD: `nodeIntegration` enabled +var win_2 = new BrowserWindow({webPreferences: {nodeIntegration: true}}); +win_2.loadURL(remote_site); -//BAD -win_3 = new BrowserWindow({ - webPreferences: { - nodeIntegrationInWorker: true - } -}); +//GOOD: `nodeIntegration` disabled +let win_3 = new BrowserWindow({webPreferences: {nodeIntegration: false}}); +win_3.loadURL(remote_site); -//BAD BrowserView -win_4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: false}}) -view = new BrowserView({ +//BAD: `nodeIntegration` enabled in the view +var win_4 = new BrowserWindow({webPreferences: {nodeIntegration: false}}) +var view_4 = new BrowserView({ webPreferences: { nodeIntegration: true } }); -win.setBrowserView(view); -view.setBounds({ x: 0, y: 0, width: 300, height: 300 }); -view.webContents.loadURL('https://untrusted-site.com'); - +win_4.setBrowserView(view_4); +view_4.webContents.loadURL(remote_site); From 1d793c0a7b853ddb0ae64622cfcee310ccd7865c Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 19 Sep 2018 14:33:23 +0100 Subject: [PATCH 067/110] JavaScript: fix expected output --- .../ServerSideUrlRedirect/ServerSideUrlRedirect.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected index 2bf3cc7dd5b3..fef8354768b5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected +++ b/javascript/ql/test/query-tests/Security/CWE-601/ServerSideUrlRedirect/ServerSideUrlRedirect.expected @@ -6,7 +6,7 @@ | express.js:78:16:78:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:78:19:78:37 | req.param("target") | user-provided value | | express.js:94:18:94:23 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value | | express.js:101:16:101:21 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value | -| express.js:119:16:119:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:119:17:119:30 | req.query.page | user-provided value | +| express.js:122:16:122:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:122:17:122:30 | req.query.page | user-provided value | | node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value | | node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value | | node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value | From 7d69c8445371fff1effcb69f09c4c1bdf33d5a4b Mon Sep 17 00:00:00 2001 From: yh-semmle Date: Wed, 19 Sep 2018 11:04:21 -0400 Subject: [PATCH 068/110] Java: tweak some query metadata The severity of four queries is reduced to `warning`. --- java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql | 2 +- java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql | 2 +- java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql | 2 +- java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql b/java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql index ffbe8f344290..eca0dc5f041a 100644 --- a/java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql +++ b/java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql @@ -3,7 +3,7 @@ * @description If a synchronized method is overridden in a subclass, and the overriding method is * not synchronized, the thread-safety of the subclass may be broken. * @kind problem - * @problem.severity error + * @problem.severity warning * @precision very-high * @id java/non-sync-override * @tags reliability diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql index 5da821a76260..1fd9bddc7bfd 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql @@ -3,7 +3,7 @@ * @description A resource that is opened for reading but not closed may cause a resource * leak. * @kind problem - * @problem.severity error + * @problem.severity warning * @precision high * @id java/input-resource-leak * @tags efficiency diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql index d2681cbb6d1a..a2803d7525ec 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql @@ -2,7 +2,7 @@ * @name Potential database resource leak * @description A database resource that is opened but not closed may cause a resource leak. * @kind problem - * @problem.severity error + * @problem.severity warning * @precision high * @id java/database-resource-leak * @tags correctness diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql index f0ce36cc8216..f854c6c7ab62 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql @@ -3,7 +3,7 @@ * @description A resource that is opened for writing but not closed may cause a resource * leak. * @kind problem - * @problem.severity error + * @problem.severity warning * @precision high * @id java/output-resource-leak * @tags efficiency From bd156757d3dcb4069657b6c78aa17f441cd51bfd Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 19 Sep 2018 14:26:17 -0700 Subject: [PATCH 069/110] C++: Remove accidental add of IR.md --- cpp/ql/src/semmle/code/cpp/ir/IR.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 cpp/ql/src/semmle/code/cpp/ir/IR.md diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.md b/cpp/ql/src/semmle/code/cpp/ir/IR.md deleted file mode 100644 index e40e04d4a3d2..000000000000 --- a/cpp/ql/src/semmle/code/cpp/ir/IR.md +++ /dev/null @@ -1,7 +0,0 @@ -# The C/C++ Intermediate Representation - -## Introduction - -The Intermediate Representation (IR) library provides a representation of the semantics of the program, independent of the syntax used to express those semantics. The IR is similar in design to representations used in optimizing compilers, such as LLVM IR. - -## Memory Access From b12c739915f7917ca1b458428ac598bf6c8e85f0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 19 Sep 2018 11:34:37 -0700 Subject: [PATCH 070/110] JavaScript: Normalize line endings of .js and .html files Added .gitattributes files for the two directories where we intentionally have line endings other than LF --- .editorconfig | 2 +- .gitattributes | 2 + .../test/library-tests/Lines/.gitattributes | 2 + .../AlertSuppression/.gitattributes | 3 + .../AlertSuppression/tstWindows.html | 14 ++--- .../AlertSuppression/tstWindows.js | 56 +++++++++---------- 6 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 javascript/ql/test/library-tests/Lines/.gitattributes create mode 100644 javascript/ql/test/query-tests/AlertSuppression/.gitattributes diff --git a/.editorconfig b/.editorconfig index 476ae898a0f8..eead7fb34a9c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp}] +[*.{ql,qll,qlref,dbscheme,qhelp,js,html}] end_of_line = lf diff --git a/.gitattributes b/.gitattributes index a6c5703f96bc..500108dbffa7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,5 @@ *.qlref eol=lf *.dbscheme eol=lf *.qhelp eol=lf +*.html eol=lf +*.js eol=lf diff --git a/javascript/ql/test/library-tests/Lines/.gitattributes b/javascript/ql/test/library-tests/Lines/.gitattributes new file mode 100644 index 000000000000..4d67c9960a4f --- /dev/null +++ b/javascript/ql/test/library-tests/Lines/.gitattributes @@ -0,0 +1,2 @@ +# This file intentionally contains a mix of different line endings +tst1.js -text diff --git a/javascript/ql/test/query-tests/AlertSuppression/.gitattributes b/javascript/ql/test/query-tests/AlertSuppression/.gitattributes new file mode 100644 index 000000000000..c2f08d06d7e8 --- /dev/null +++ b/javascript/ql/test/query-tests/AlertSuppression/.gitattributes @@ -0,0 +1,3 @@ +# These files intentionally contain CRLF line endings. +tstWindows.html eol=crlf +tstWindows.js eol=crlf diff --git a/javascript/ql/test/query-tests/AlertSuppression/tstWindows.html b/javascript/ql/test/query-tests/AlertSuppression/tstWindows.html index 94ce5a077ee0..ec556d0b278e 100644 --- a/javascript/ql/test/query-tests/AlertSuppression/tstWindows.html +++ b/javascript/ql/test/query-tests/AlertSuppression/tstWindows.html @@ -1,7 +1,7 @@ - - Title - -
    -
    - - + + Title + +
    +
    + + diff --git a/javascript/ql/test/query-tests/AlertSuppression/tstWindows.js b/javascript/ql/test/query-tests/AlertSuppression/tstWindows.js index 80ad004b11bf..1bbb7d4c5e84 100644 --- a/javascript/ql/test/query-tests/AlertSuppression/tstWindows.js +++ b/javascript/ql/test/query-tests/AlertSuppression/tstWindows.js @@ -1,28 +1,28 @@ -debugger; // lgtm -// lgtm[js/debugger-statement] -// lgtm[js/debugger-statement, js/invocation-of-non-function] -// lgtm[@tag:nullness] -// lgtm[@tag:nullness,js/debugger-statement] -// lgtm[@expires:2017-06-11] -// lgtm[js/invocation-of-non-function] because I know better than lgtm -// lgtm: blah blah -// lgtm blah blah #falsepositive -//lgtm [js/invocation-of-non-function] -/* lgtm */ -// lgtm[] -// lgtmfoo -//lgtm -// lgtm -// lgtm [js/debugger-statement] -// foolgtm[js/debugger-statement] -// foolgtm -// foo; lgtm -// foo; lgtm[js/debugger-statement] -// foo lgtm -// foo lgtm[js/debugger-statement] -// foo lgtm bar -// foo lgtm[js/debugger-statement] bar -// LGTM! -// LGTM[js/debugger-statement] -// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function] -// lgtm[js/debugger-statement]; lgtm +debugger; // lgtm +// lgtm[js/debugger-statement] +// lgtm[js/debugger-statement, js/invocation-of-non-function] +// lgtm[@tag:nullness] +// lgtm[@tag:nullness,js/debugger-statement] +// lgtm[@expires:2017-06-11] +// lgtm[js/invocation-of-non-function] because I know better than lgtm +// lgtm: blah blah +// lgtm blah blah #falsepositive +//lgtm [js/invocation-of-non-function] +/* lgtm */ +// lgtm[] +// lgtmfoo +//lgtm +// lgtm +// lgtm [js/debugger-statement] +// foolgtm[js/debugger-statement] +// foolgtm +// foo; lgtm +// foo; lgtm[js/debugger-statement] +// foo lgtm +// foo lgtm[js/debugger-statement] +// foo lgtm bar +// foo lgtm[js/debugger-statement] bar +// LGTM! +// LGTM[js/debugger-statement] +// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function] +// lgtm[js/debugger-statement]; lgtm From 2b9f42b30875e12af7e2d2f403272e5ece62e7d1 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 19 Sep 2018 12:02:18 -0700 Subject: [PATCH 071/110] JavaScript: Force LF for .json and .yml --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index 500108dbffa7..69ff97dca0c0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,3 +17,5 @@ *.qhelp eol=lf *.html eol=lf *.js eol=lf +*.json eol=lf +*.yml eol=lf From 524c67c3fbf152f4d550c6e46cdd5737dddcdf29 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 19 Sep 2018 13:07:02 -0700 Subject: [PATCH 072/110] JavaScript: Normalize .ts line endings to LF --- .editorconfig | 2 +- .gitattributes | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index eead7fb34a9c..c6cc3fea3570 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp,js,html}] +[*.{ql,qll,qlref,dbscheme,qhelp,html,js,ts,json,yml}] end_of_line = lf diff --git a/.gitattributes b/.gitattributes index 69ff97dca0c0..80652c576f82 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,5 +17,6 @@ *.qhelp eol=lf *.html eol=lf *.js eol=lf +*.ts eol=lf *.json eol=lf *.yml eol=lf From e06969ddb4605ad288b51bea8e87232b2029adb7 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 19 Sep 2018 20:18:57 -0700 Subject: [PATCH 073/110] JavaScript: Normalize .mjs files to LF --- .editorconfig | 2 +- .gitattributes | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index c6cc3fea3570..decc2081a24a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp,html,js,ts,json,yml}] +[*.{ql,qll,qlref,dbscheme,qhelp,html,js,mjs,ts,json,yml}] end_of_line = lf diff --git a/.gitattributes b/.gitattributes index 80652c576f82..ec825a38b579 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,6 +17,7 @@ *.qhelp eol=lf *.html eol=lf *.js eol=lf +*.mjs eol=lf *.ts eol=lf *.json eol=lf *.yml eol=lf From 27cee9bd80a10066a6a807b3ec07f3e895d7a67b Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 20 Sep 2018 08:00:38 -0700 Subject: [PATCH 074/110] C++: Handle inheritance conversions in IR GVN --- .../aliased_ssa/internal/gvn/ValueNumber.qll | 26 +++++--- .../raw/internal/gvn/ValueNumber.qll | 26 +++++--- .../internal/gvn/ValueNumber.qll | 26 +++++--- .../GlobalValueNumbering.expected | 3 + .../GlobalValueNumbering/ir_gvn.expected | 59 +++++++++++++++++++ .../GlobalValueNumbering/test.cpp | 16 +++++ 6 files changed, 132 insertions(+), 24 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll index e682c417ba2a..1f91e6cac1a8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll @@ -45,6 +45,10 @@ newtype TValueNumber = TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { unaryValueNumber(_, funcIR, opcode, type, operand) } or + TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass, + Class derivedClass, ValueNumber operand) { + inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand) + } or TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { uniqueValueNumber(instr, funcIR) } @@ -81,13 +85,6 @@ class ValueNumber extends TValueNumber { } } -class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} -class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} -class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} -class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} -class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} -class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} - /** * A `CopyInstruction` whose source operand's value is congruent to the definition of that source * operand. @@ -187,12 +184,20 @@ private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Op Type type, ValueNumber operand) { instr.getFunctionIR() = funcIR and (not instr instanceof InheritanceConversionInstruction) and - (not instr instanceof FieldAddressInstruction) and instr.getOpcode() = opcode and instr.getResultType() = type and valueNumber(instr.getOperand()) = operand } +private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr, + FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getBaseClass() = baseClass and + instr.getDerivedClass() = derivedClass and + valueNumber(instr.getOperand()) = operand +} + /** * Holds if `instr` should be assigned a unique value number because this library does not know how * to determine if two instances of that instruction are equivalent. @@ -250,6 +255,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { unaryValueNumber(instr, funcIR, opcode, type, operand) and result = TUnaryValueNumber(funcIR, opcode, type, operand) ) or + exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass, + operand) and + result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand) + ) or exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand | pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll index e682c417ba2a..1f91e6cac1a8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll @@ -45,6 +45,10 @@ newtype TValueNumber = TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { unaryValueNumber(_, funcIR, opcode, type, operand) } or + TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass, + Class derivedClass, ValueNumber operand) { + inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand) + } or TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { uniqueValueNumber(instr, funcIR) } @@ -81,13 +85,6 @@ class ValueNumber extends TValueNumber { } } -class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} -class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} -class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} -class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} -class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} -class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} - /** * A `CopyInstruction` whose source operand's value is congruent to the definition of that source * operand. @@ -187,12 +184,20 @@ private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Op Type type, ValueNumber operand) { instr.getFunctionIR() = funcIR and (not instr instanceof InheritanceConversionInstruction) and - (not instr instanceof FieldAddressInstruction) and instr.getOpcode() = opcode and instr.getResultType() = type and valueNumber(instr.getOperand()) = operand } +private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr, + FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getBaseClass() = baseClass and + instr.getDerivedClass() = derivedClass and + valueNumber(instr.getOperand()) = operand +} + /** * Holds if `instr` should be assigned a unique value number because this library does not know how * to determine if two instances of that instruction are equivalent. @@ -250,6 +255,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { unaryValueNumber(instr, funcIR, opcode, type, operand) and result = TUnaryValueNumber(funcIR, opcode, type, operand) ) or + exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass, + operand) and + result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand) + ) or exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand | pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll index e682c417ba2a..1f91e6cac1a8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll @@ -45,6 +45,10 @@ newtype TValueNumber = TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) { unaryValueNumber(_, funcIR, opcode, type, operand) } or + TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass, + Class derivedClass, ValueNumber operand) { + inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand) + } or TUniqueValueNumber(FunctionIR funcIR, Instruction instr) { uniqueValueNumber(instr, funcIR) } @@ -81,13 +85,6 @@ class ValueNumber extends TValueNumber { } } -class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {} -class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {} -class ConstantValueNumber extends ValueNumber, TConstantValueNumber {} -class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {} -class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {} -class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {} - /** * A `CopyInstruction` whose source operand's value is congruent to the definition of that source * operand. @@ -187,12 +184,20 @@ private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Op Type type, ValueNumber operand) { instr.getFunctionIR() = funcIR and (not instr instanceof InheritanceConversionInstruction) and - (not instr instanceof FieldAddressInstruction) and instr.getOpcode() = opcode and instr.getResultType() = type and valueNumber(instr.getOperand()) = operand } +private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr, + FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) { + instr.getFunctionIR() = funcIR and + instr.getOpcode() = opcode and + instr.getBaseClass() = baseClass and + instr.getDerivedClass() = derivedClass and + valueNumber(instr.getOperand()) = operand +} + /** * Holds if `instr` should be assigned a unique value number because this library does not know how * to determine if two instances of that instruction are equivalent. @@ -250,6 +255,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { unaryValueNumber(instr, funcIR, opcode, type, operand) and result = TUnaryValueNumber(funcIR, opcode, type, operand) ) or + exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass, + operand) and + result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand) + ) or exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand | pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand, diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/GlobalValueNumbering.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/GlobalValueNumbering.expected index 9969baf40a81..d7c3740aed37 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/GlobalValueNumbering.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/GlobalValueNumbering.expected @@ -27,3 +27,6 @@ | test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 | | test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 79:c7-c7 | | test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | +| test.cpp:105:11:105:12 | (Base *)... | 105:c11-c12 106:c14-c35 107:c11-c12 | +| test.cpp:105:11:105:12 | pd | 105:c11-c12 106:c33-c34 | +| test.cpp:105:15:105:15 | b | 105:c15-c15 107:c15-c15 109:c10-c10 | diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 35b507a61f17..3df04157bf62 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -653,3 +653,62 @@ test.cpp: # 91| v0_12(void) = ReturnValue : r0_11, m0_10 # 91| v0_13(void) = UnmodeledUse : mu* # 91| v0_14(void) = ExitFunction : + +# 104| inheritanceConversions(Derived *) -> int +# 104| Block 0 +# 104| v0_0(void) = EnterFunction : +# 104| mu0_1(unknown) = UnmodeledDefinition : +# 104| valnum = unique +# 104| r0_2(glval) = VariableAddress[pd] : +# 104| valnum = r0_2 +# 104| m0_3(Derived *) = InitializeParameter[pd] : r0_2 +# 104| valnum = m0_3 +# 105| r0_4(glval) = VariableAddress[x] : +# 105| valnum = unique +# 105| r0_5(glval) = VariableAddress[pd] : +# 105| valnum = r0_2 +# 105| r0_6(Derived *) = Load : r0_5, m0_3 +# 105| valnum = m0_3 +# 105| r0_7(Base *) = ConvertToBase[Derived : Base] : r0_6 +# 105| valnum = r0_7 +# 105| r0_8(glval) = FieldAddress[b] : r0_7 +# 105| valnum = r0_8 +# 105| r0_9(int) = Load : r0_8, mu0_1 +# 105| valnum = r0_9 +# 105| m0_10(int) = Store : r0_4, r0_9 +# 105| valnum = r0_9 +# 106| r0_11(glval) = VariableAddress[pb] : +# 106| valnum = r0_11 +# 106| r0_12(glval) = VariableAddress[pd] : +# 106| valnum = r0_2 +# 106| r0_13(Derived *) = Load : r0_12, m0_3 +# 106| valnum = m0_3 +# 106| r0_14(Base *) = ConvertToBase[Derived : Base] : r0_13 +# 106| valnum = r0_7 +# 106| m0_15(Base *) = Store : r0_11, r0_14 +# 106| valnum = r0_7 +# 107| r0_16(glval) = VariableAddress[y] : +# 107| valnum = r0_16 +# 107| r0_17(glval) = VariableAddress[pb] : +# 107| valnum = r0_11 +# 107| r0_18(Base *) = Load : r0_17, m0_15 +# 107| valnum = r0_7 +# 107| r0_19(glval) = FieldAddress[b] : r0_18 +# 107| valnum = r0_8 +# 107| r0_20(int) = Load : r0_19, mu0_1 +# 107| valnum = r0_20 +# 107| m0_21(int) = Store : r0_16, r0_20 +# 107| valnum = r0_20 +# 109| r0_22(glval) = VariableAddress[#return] : +# 109| valnum = r0_22 +# 109| r0_23(glval) = VariableAddress[y] : +# 109| valnum = r0_16 +# 109| r0_24(int) = Load : r0_23, m0_21 +# 109| valnum = r0_20 +# 109| m0_25(int) = Store : r0_22, r0_24 +# 109| valnum = r0_20 +# 104| r0_26(glval) = VariableAddress[#return] : +# 104| valnum = r0_22 +# 104| v0_27(void) = ReturnValue : r0_26, m0_25 +# 104| v0_28(void) = UnmodeledUse : mu* +# 104| v0_29(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp index 9c4a8bdd2548..2003a299aabf 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp @@ -92,3 +92,19 @@ int regression_test00() { int x = x = 10; return x; } + +struct Base { + int b; +}; + +struct Derived : Base { + int d; +}; + +int inheritanceConversions(Derived* pd) { + int x = pd->b; + Base* pb = static_cast(pd); + int y = pb->b; + + return y; +} \ No newline at end of file From 5a25602c28a95987ec2fbcc8738fd6e9a6c7e5c4 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 20 Sep 2018 08:21:15 -0700 Subject: [PATCH 075/110] C++: Move GVN out of "internal" directory --- config/identical-files.json | 6 +++--- cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll | 1 + .../gvn/ValueNumber.qll => gvn/ValueNumbering.qll} | 2 +- .../internal/ValueNumberingInternal.qll} | 0 .../gvn/ValueNumber.qll => gvn/ValueNumbering.qll} | 2 +- .../internal/ValueNumberingInternal.qll} | 0 .../gvn/ValueNumber.qll => gvn/ValueNumbering.qll} | 2 +- .../internal/ValueNumberingInternal.qll} | 0 .../valuenumbering/GlobalValueNumbering/ir_gvn.ql | 2 +- 9 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll rename cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/{internal/gvn/ValueNumber.qll => gvn/ValueNumbering.qll} (99%) rename cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/{internal/gvn/ValueNumberInternal.qll => gvn/internal/ValueNumberingInternal.qll} (100%) rename cpp/ql/src/semmle/code/cpp/ir/implementation/raw/{internal/gvn/ValueNumber.qll => gvn/ValueNumbering.qll} (99%) rename cpp/ql/src/semmle/code/cpp/ir/implementation/raw/{internal/gvn/ValueNumberInternal.qll => gvn/internal/ValueNumberingInternal.qll} (100%) rename cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/{internal/gvn/ValueNumber.qll => gvn/ValueNumbering.qll} (99%) rename cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/{internal/gvn/ValueNumberInternal.qll => gvn/internal/ValueNumberingInternal.qll} (100%) diff --git a/config/identical-files.json b/config/identical-files.json index c7c6ba4bc01f..829837ceed40 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -56,8 +56,8 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" ], "C++ IR ValueNumber": [ - "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll" + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll" ] } diff --git a/cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll new file mode 100644 index 000000000000..bd02afc58fba --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll @@ -0,0 +1 @@ +import implementation.aliased_ssa.gvn.ValueNumbering diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll similarity index 99% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll index 1f91e6cac1a8..cff349a07b44 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll @@ -1,4 +1,4 @@ -private import ValueNumberInternal +private import internal.ValueNumberingInternal import cpp private import IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumberInternal.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll similarity index 99% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll index 1f91e6cac1a8..cff349a07b44 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll @@ -1,4 +1,4 @@ -private import ValueNumberInternal +private import internal.ValueNumberingInternal import cpp private import IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumberInternal.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll similarity index 99% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll index 1f91e6cac1a8..cff349a07b44 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll @@ -1,4 +1,4 @@ -private import ValueNumberInternal +private import internal.ValueNumberingInternal import cpp private import IR diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll similarity index 100% rename from cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumberInternal.qll rename to cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql index 2b9521af1ae1..97d59c733317 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql @@ -3,4 +3,4 @@ */ import semmle.code.cpp.ir.PrintIR import semmle.code.cpp.ir.IR -import semmle.code.cpp.ir.implementation.aliased_ssa.internal.gvn.ValueNumber +import semmle.code.cpp.ir.ValueNumbering From b0ec929aaddf9e72b1380c0759ddfb1aefffff84 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Thu, 20 Sep 2018 16:16:32 -0700 Subject: [PATCH 076/110] Cast between semantically different integer types: HRESULT to/from a Boolean type. Closing the gap between Semmle and PreFast. Covers C6214, C6215, C6216, C6217, C6230 --- .gitignore | 4 + .../CWE/CWE-704/IncorrectTypeConversion.cpp | 7 ++ .../CWE/CWE-704/IncorrectTypeConversion.qhelp | 26 +++++++ .../CWE/CWE-704/IncorrectTypeConversion.ql | 30 ++++++++ .../CWE/CWE-704/IncorrectTypeConversion.cpp | 76 +++++++++++++++++++ .../CWE-704/IncorrectTypeConversion.expected | 6 ++ .../CWE/CWE-704/IncorrectTypeConversion.qlref | 1 + 7 files changed, 150 insertions(+) create mode 100644 cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp create mode 100644 cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp create mode 100644 cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref diff --git a/.gitignore b/.gitignore index 31f8ccd9abf2..5fa8568d5b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ # qltest projects and artifacts */ql/test/**/*.testproj */ql/test/**/*.actual +/.vs/slnx.sqlite +/.vs/ql2/v15/Browse.VC.opendb +/.vs/ql2/v15/Browse.VC.db +/.vs/ProjectSettings.json diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp new file mode 100644 index 000000000000..bf711c9b2886 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp @@ -0,0 +1,7 @@ +LPMALLOC pMalloc; +HRESULT hr = CoGetMalloc(1, &pMalloc); + +if (!hr) +{ + // code ... +} diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp new file mode 100644 index 000000000000..d441e368b20c --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp @@ -0,0 +1,26 @@ + + + + +

    This query indicates that an HRESULT is being cast to a boolean type or vice versa.

    +

    The typical success value (S_OK) of an HRESULT equals 0. However, 0 indicates failure for a boolean type.

    +

    Casting an HRESULT to a boolean type and then using it in a test expression will yield an incorrect result.

    +
    + + +

    To check if a call that returns an HRESULT succeeded use the FAILED macro.

    +
    + + +

    In the following example, HRESULT is used in a test expression incorrectly as it may yield an incorrect result.

    + + +

    To fix this issue, use the FAILED macro in the test expression.

    +
    + + + + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql new file mode 100644 index 000000000000..900ee6ace2fc --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql @@ -0,0 +1,30 @@ +/** + * @name Cast between semantically different integer types: HRESULT to/from a Boolean type + * @description Cast between semantically different integer types: HRESULT to/from a Boolean type. + * Boolean types indicate success by a non-zero value, whereas success (S_OK) in HRESULT is indicated by a value of 0. + * Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result. + * @kind problem + * @id cpp/incorrect-type-conversion + * @problem.severity error + * @precision high + * @tags security + * external/cwe/cwe-704 + * external/microsoft/C6214 + * external/microsoft/C6215 + * external/microsoft/C6216 + * external/microsoft/C6217 + * external/microsoft/C6230 + */ +import cpp + +from Expr e1, Cast e2, string msg +where e2 = e1.getConversion() and + exists ( Type t1, Type t2 | + t1 = e1.getType() and + t2 = e2.getType() and + ((t1.hasName("bool") or t1.hasName("BOOL")) and t2.hasName("HRESULT") or + (t2.hasName("bool") or t2.hasName("BOOL")) and t1.hasName("HRESULT") + )) + and if e2.isImplicit() then ( msg = "Implicit" ) + else ( msg = "Explicit" ) +select e1, msg + " conversion from " + e1.getType().toString() + " to " + e2.getType().toString() \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp new file mode 100644 index 000000000000..cb29d8cd742f --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp @@ -0,0 +1,76 @@ +// winnt.h +typedef long HRESULT; +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) + +// minwindef.h +typedef int BOOL; +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +// winerror.h +#define S_OK ((HRESULT)0L) +#define S_FALSE ((HRESULT)1L) +#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL) + +HRESULT HresultFunction() +{ + return S_OK; +} + +BOOL BoolFunction() +{ + return FALSE; +} + +HRESULT IncorrectHresultFunction() +{ + return BoolFunction(); // BUG +} + +void IncorrectTypeConversionTest() { + HRESULT hr = HresultFunction(); + if ((BOOL)hr) // BUG + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } + + if (SUCCEEDED(BoolFunction())) // BUG + { + // ... + } + if (BoolFunction()) // Correct Usage + { + // ... + } + BOOL b = IncorrectHresultFunction(); // BUG + + hr = E_UNEXPECTED; + if (!hr) // BUG + { + // ... + } + if (!FAILED(hr)) // Correct Usage + { + // ... + } + + hr = S_FALSE; + if (hr) // Should fail + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected new file mode 100644 index 000000000000..35c40158a771 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected @@ -0,0 +1,6 @@ +| IncorrectTypeConversion.cpp:33:9:33:20 | call to BoolFunction | Implicit conversion from BOOL to HRESULT | +| IncorrectTypeConversion.cpp:38:12:38:13 | hr | Explicit conversion from HRESULT to BOOL | +| IncorrectTypeConversion.cpp:47:6:47:30 | (...) | Explicit conversion from BOOL to HRESULT | +| IncorrectTypeConversion.cpp:55:11:55:34 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to BOOL | +| IncorrectTypeConversion.cpp:58:7:58:8 | hr | Implicit conversion from HRESULT to bool | +| IncorrectTypeConversion.cpp:68:6:68:7 | hr | Implicit conversion from HRESULT to bool | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref new file mode 100644 index 000000000000..cd59f148bd02 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-704/incorrectTypeConversion.ql \ No newline at end of file From 48c99fb1d11db7c1d17ec203b691afaf10d3cf32 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Thu, 20 Sep 2018 16:28:37 -0700 Subject: [PATCH 077/110] =?UTF-8?q?Setting=20a=20SECURITY=5FDESCRIPTOR?= =?UTF-8?q?=E2=80=99s=20DACL=20to=20NULL=20Closing=20the=20gap=20between?= =?UTF-8?q?=20Semmle=20&=20PreFAST=20This=20rule=20is=20equivalent=20to=20?= =?UTF-8?q?C6248?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + .../CWE-732/UnsafeDaclSecurityDescriptor.cpp | 14 +++ .../UnsafeDaclSecurityDescriptor.qhelp | 29 ++++++ .../CWE-732/UnsafeDaclSecurityDescriptor.ql | 61 +++++++++++++ .../CWE-732/UnsafeDaclSecurityDescriptor.cpp | 91 +++++++++++++++++++ .../UnsafeDaclSecurityDescriptor.expected | 2 + .../UnsafeDaclSecurityDescriptor.qlref | 1 + 7 files changed, 202 insertions(+) create mode 100644 cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp create mode 100644 cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp create mode 100644 cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qlref diff --git a/.gitignore b/.gitignore index 31f8ccd9abf2..f77f6b11d935 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ # qltest projects and artifacts */ql/test/**/*.testproj */ql/test/**/*.actual +/.vs/slnx.sqlite +/.vs/ql3/v15/Browse.VC.opendb +/.vs/ql3/v15/Browse.VC.db +/.vs/ProjectSettings.json diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp new file mode 100644 index 000000000000..590f1482e8f6 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -0,0 +1,14 @@ +SECURITY_DESCRIPTOR pSD; +SECURITY_ATTRIBUTES SA; + +if (!InitializeSecurityDescriptor(&pSD, SECURITY_DESCRIPTOR_REVISION)) +{ + // error handling +} +if (!SetSecurityDescriptorDacl(&pSD, + TRUE, // bDaclPresent - this value indicates the presence of a DACL in the security descriptor + NULL, // pDacl - the pDacl parameter does not point to a DACL. All access will be allowed + FALSE)) +{ + // error handling +} diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp new file mode 100644 index 000000000000..79e2285201aa --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp @@ -0,0 +1,29 @@ + + + + +

    This query indicates that a call is setting the SECURITY_DESCRIPTOR's DACL field to null.

    +

    When using SetSecurityDescriptorDacl to set a discretionary access control (DACL), setting the bDaclPresent argument to TRUE indicates the prescence of a DACL in the security description in the argument pDacl.

    +

    When the pDacl parameter does not point to a DACL (i.e. it is NULL) and the bDaclPresent flag is TRUE, a NULL DACL is specified.

    +

    A NULL DACL grants full access to any user who requests it; normal security checking is not performed with respect to the object.

    +
    + + +

    You should not use a NULL DACL with an object because any user can change the DACL and owner of the security descriptor.

    +
    + + +

    In the following example, the call to SetSecurityDescriptorDacl is setting an unsafe DACL (NULL DACL) to the security descriptor.

    + + +

    To fix this issue, pDacl argument should be a pointer to an ACL structure that specifies the DACL for the security descriptor.

    +
    + + +
  • SetSecurityDescriptorDacl function (Microsoft documentation). +
  • +
    + +
    diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql new file mode 100644 index 000000000000..19551a46c5be --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -0,0 +1,61 @@ +/** + * @name Setting a SECURITY_DESCRIPTOR’s DACL to NULL + * @description Setting a SECURITY_DESCRIPTOR’s DACL to NULL will result in an unprotected object. + * If the DACL that belongs to the security descriptor of an object is set to NULL, a null DACL is created. + * A null DACL grants full access to any user who requests it; + * normal security checking is not performed with respect to the object. + * @id cpp/unsafe-dacl-security-descriptor + * @kind problem + * @problem.severity error + * @precision high + * @tags security + * external/cwe/cwe-732 + * external/microsoft/C6248 + */ +import cpp +import semmle.code.cpp.dataflow.DataFlow + +/** + * A function call to SetSecurityDescriptorDacl to set the ACL, specified by (2nd argument) bDaclPresent = TRUE + */ +class SetSecurityDescriptorDaclFunctionCall extends FunctionCall { + SetSecurityDescriptorDaclFunctionCall() { + this.getTarget().hasGlobalName("SetSecurityDescriptorDacl") + and this.getArgument(1).getValue().toInt() != 0 + } +} + +/** + * Dataflow that detects a call to SetSecurityDescriptorDacl with a NULL DACL as the pDacl argument + */ +class SetSecurityDescriptorDaclFunctionConfiguration extends DataFlow::Configuration { + SetSecurityDescriptorDaclFunctionConfiguration() { + this = "SetSecurityDescriptorDaclFunctionConfiguration" + } + + override predicate isSource(DataFlow::Node source) { + exists( NullValue nullExpr | + source.asExpr() = nullExpr + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists( SetSecurityDescriptorDaclFunctionCall call, VariableAccess val | + val = sink.asExpr() | + val = call.getArgument(2) + ) + } +} + +from SetSecurityDescriptorDaclFunctionCall call, string message +where exists( NullValue nullExpr | + message = "Setting a SECURITY_DESCRIPTOR’s DACL to NULL will result in an unprotected object." | + call.getArgument(1).getValue().toInt() != 0 + and call.getArgument(2) = nullExpr + ) or exists( Expr constassign, VariableAccess var, + SetSecurityDescriptorDaclFunctionConfiguration config | + message = "Setting a SECURITY_DESCRIPTOR’s DACL using variable " + var + " that is set to NULL will result in an unprotected object." | + var = call.getArgument(2) + and config.hasFlow(DataFlow::exprNode(constassign), DataFlow::exprNode(var)) + ) +select call, message \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp new file mode 100644 index 000000000000..9fe34cf5d5cc --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -0,0 +1,91 @@ +typedef unsigned long DWORD; +typedef unsigned long ULONG; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef int BOOL; +typedef void *PVOID; +#define TRUE 1 +#define FALSE 0 +#define ERROR_SUCCESS 0L +#define NULL 0 + +typedef PVOID PSECURITY_DESCRIPTOR; + +typedef struct _ACL { + BYTE AclRevision; + BYTE Sbz1; + WORD AclSize; + WORD AceCount; + WORD Sbz2; +} ACL; +typedef ACL *PACL; + +typedef enum _ACCESS_MODE +{ + NOT_USED_ACCESS = 0, + GRANT_ACCESS, + SET_ACCESS, + DENY_ACCESS, + REVOKE_ACCESS, + SET_AUDIT_SUCCESS, + SET_AUDIT_FAILURE +} ACCESS_MODE; + +typedef int TRUSTEE_W; + +typedef struct _EXPLICIT_ACCESS_W +{ + DWORD grfAccessPermissions; + ACCESS_MODE grfAccessMode; + DWORD grfInheritance; + TRUSTEE_W Trustee; +} EXPLICIT_ACCESS_W, *PEXPLICIT_ACCESS_W, EXPLICIT_ACCESSW, *PEXPLICIT_ACCESSW; + +BOOL +SetSecurityDescriptorDacl( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + BOOL bDaclPresent, + PACL pDacl, + BOOL bDaclDefaulted +) { + return TRUE; +} + +DWORD SetEntriesInAcl( + ULONG cCountOfExplicitEntries, + PEXPLICIT_ACCESS_W pListOfExplicitEntries, + PACL OldAcl, + PACL *NewAcl +) +{ + *NewAcl = (PACL)0xFFFFFF; + return ERROR_SUCCESS; +} + +void Test() +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor; + BOOL b; + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + NULL, // NULL pointer to DACL == BUG + FALSE); + + PACL pDacl = NULL; + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + pDacl, // NULL pointer to DACL == BUG + FALSE); + + SetEntriesInAcl(0, NULL, NULL, &pDacl); + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + pDacl, // Should have been set by SetEntriesInAcl ==> should not be flagged + FALSE); + + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + FALSE, // Dacl is not Present + NULL, // DACL is going to be removed from security descriptor. Default/inherited access ==> should not be flagged + FALSE); + +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected new file mode 100644 index 000000000000..dd4b1db43c11 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected @@ -0,0 +1,2 @@ +| UnsafeDaclSecurityDescriptor.cpp:69:6:69:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:75:6:75:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL using variable pDacl that is set to NULL will result in an unprotected object. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qlref new file mode 100644 index 000000000000..6d8a0fc40192 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql \ No newline at end of file From c7aa5c169be733ac28e8ba64b5655782d9e5ab7c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 21 Sep 2018 10:47:00 +0100 Subject: [PATCH 078/110] CPP: Add a test of placement new for AV Rule 79.ql. --- .../AV Rule 79/AV Rule 79.expected | 3 ++ .../4.10 Classes/AV Rule 79/PlacementNew.cpp | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected index 85f71ce64468..e50f34976971 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected @@ -11,5 +11,8 @@ | ExternalOwners.cpp:49:3:49:20 | ... = ... | Resource a is acquired by class MyScreen but not released anywhere in this class. | | ListDelete.cpp:21:3:21:21 | ... = ... | Resource first is acquired by class MyThingColection but not released anywhere in this class. | | NoDestructor.cpp:23:3:23:20 | ... = ... | Resource n is acquired by class MyClass5 but not released anywhere in this class. | +| PlacementNew.cpp:36:3:36:36 | ... = ... | Resource p1 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | +| PlacementNew.cpp:37:3:37:51 | ... = ... | Resource p2 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | +| PlacementNew.cpp:38:3:38:49 | ... = ... | Resource p3 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | | SelfRegistering.cpp:25:3:25:24 | ... = ... | Resource side is acquired by class MyOwner but not released anywhere in this class. | | Variants.cpp:23:3:23:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp new file mode 100644 index 000000000000..46704715498b --- /dev/null +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp @@ -0,0 +1,47 @@ + +typedef unsigned long size_t; + +namespace std +{ + using ::size_t; + struct nothrow_t {}; + extern const nothrow_t nothrow; +} + +// nothrow new +void* operator new(std::size_t size, const std::nothrow_t&) throw(); + +// placement new +void* operator new (std::size_t size, void* ptr) throw(); + +// --- + +class MyClassForPlacementNew +{ +public: + MyClassForPlacementNew(int _v) : v(_v) {} + ~MyClassForPlacementNew() {} + +private: + int v; +}; + +class MyTestForPlacementNew +{ +public: + MyTestForPlacementNew() + { + void *buffer_ptr = buffer; + + p1 = new MyClassForPlacementNew(1); // BAD: not released + p2 = new (std::nothrow) MyClassForPlacementNew(2); // BAD: not released + p3 = new (buffer_ptr) MyClassForPlacementNew(3); // GOOD: placement new, not an allocation [FALSE POSITIVE] + } + + ~MyTestForPlacementNew() + { + } + + MyClassForPlacementNew *p1, *p2, *p3; + char buffer[sizeof(MyClassForPlacementNew)]; +}; From 84f9900c8c0cf4224718a6f6563993d4623fd65a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 21 Sep 2018 10:53:42 +0100 Subject: [PATCH 079/110] CPP: Exclude placement new. --- cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql | 16 +++++++++++++++- .../4.10 Classes/AV Rule 79/AV Rule 79.expected | 2 -- .../jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp | 4 ++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql index 22ee7204b91c..3e9c6b1cccf7 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql @@ -125,7 +125,21 @@ class Resource extends MemberVariable { private Assignment getANew() { result.getLValue() = this.getAnAccess() and - (result.getRValue() instanceof NewExpr or result.getRValue() instanceof NewArrayExpr) and + ( + ( + result.getRValue() instanceof NewExpr and + + // exclude placement new and custom overloads as they + // may not conform to assumptions + not result.getRValue().(NewExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 + ) or ( + result.getRValue() instanceof NewArrayExpr and + + // exclude placement new and custom overloads as they + // may not conform to assumptions + not result.getRValue().(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 + ) + ) and this.inSameClass(result) } diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected index e50f34976971..2aad28d2c4eb 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected @@ -12,7 +12,5 @@ | ListDelete.cpp:21:3:21:21 | ... = ... | Resource first is acquired by class MyThingColection but not released anywhere in this class. | | NoDestructor.cpp:23:3:23:20 | ... = ... | Resource n is acquired by class MyClass5 but not released anywhere in this class. | | PlacementNew.cpp:36:3:36:36 | ... = ... | Resource p1 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | -| PlacementNew.cpp:37:3:37:51 | ... = ... | Resource p2 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | -| PlacementNew.cpp:38:3:38:49 | ... = ... | Resource p3 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | | SelfRegistering.cpp:25:3:25:24 | ... = ... | Resource side is acquired by class MyOwner but not released anywhere in this class. | | Variants.cpp:23:3:23:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp index 46704715498b..c7794857cb94 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp @@ -34,8 +34,8 @@ class MyTestForPlacementNew void *buffer_ptr = buffer; p1 = new MyClassForPlacementNew(1); // BAD: not released - p2 = new (std::nothrow) MyClassForPlacementNew(2); // BAD: not released - p3 = new (buffer_ptr) MyClassForPlacementNew(3); // GOOD: placement new, not an allocation [FALSE POSITIVE] + p2 = new (std::nothrow) MyClassForPlacementNew(2); // BAD: not released [NOT DETECTED] + p3 = new (buffer_ptr) MyClassForPlacementNew(3); // GOOD: placement new, not an allocation } ~MyTestForPlacementNew() From 6f109a742f8f120165914c198e37892038c2b6b1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Sep 2018 11:04:33 +0100 Subject: [PATCH 080/110] JS: add a test case for res.sendfile --- .../ql/test/query-tests/Security/CWE-022/TaintedPath.expected | 1 + .../ql/test/query-tests/Security/CWE-022/tainted-sendFile.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.expected index 5abd47e9abbd..418873e5c6f0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.expected @@ -25,4 +25,5 @@ | tainted-array-steps.js:15:29:15:43 | parts.join('/') | This path depends on $@. | tainted-array-steps.js:9:24:9:30 | req.url | a user-provided value | | tainted-require.js:7:19:7:37 | req.param("module") | This path depends on $@. | tainted-require.js:7:19:7:37 | req.param("module") | a user-provided value | | tainted-sendFile.js:7:16:7:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:7:16:7:33 | req.param("gimme") | a user-provided value | +| tainted-sendFile.js:9:16:9:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:9:16:9:33 | req.param("gimme") | a user-provided value | | views.js:1:43:1:55 | req.params[0] | This path depends on $@. | views.js:1:43:1:55 | req.params[0] | a user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/tainted-sendFile.js b/javascript/ql/test/query-tests/Security/CWE-022/tainted-sendFile.js index be465c8d5ef3..e925f6c566b3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/tainted-sendFile.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/tainted-sendFile.js @@ -5,4 +5,6 @@ var app = express(); app.get('/some/path', function(req, res) { // BAD: sending a file based on un-sanitized query parameters res.sendFile(req.param("gimme")); + // BAD: same as above + res.sendfile(req.param("gimme")); }); From 5f467d2fc518034ebdd08b81813fc7c86f981c70 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Sep 2018 13:15:17 +0100 Subject: [PATCH 081/110] JS: recognize CSRF middleware from lusca package --- .../Security/CWE-352/MissingCsrfMiddleware.ql | 11 ++++--- .../CWE-352/MissingCsrfMiddleware.expected | 3 ++ .../Security/CWE-352/lusca_example.js | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 9aec09aa6b67..554f0f310958 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -38,12 +38,15 @@ predicate hasCookieMiddleware(Express::RouteHandlerExpr expr, Express::RouteHand * // protected from CSRF * }) * ``` - * - * Currently the predicate only detects `csurf`-based protectors. */ DataFlow::CallNode csrfMiddlewareCreation() { - exists (DataFlow::ModuleImportNode mod | result = mod.getACall() | - mod.getPath() = "csurf" + exists (DataFlow::SourceNode callee | result = callee.getACall() | + callee = DataFlow::moduleImport("csurf") + or + callee = DataFlow::moduleImport("lusca") and + result.getOptionArgument(0, "csrf").analyze().getABooleanValue() = true // any truthy value will enable CSRF + or + callee = DataFlow::moduleMember("lusca", "csrf") ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected index 974dd70fc550..dcdbcc9d4b5c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected +++ b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected @@ -1,3 +1,6 @@ | MissingCsrfMiddlewareBad.js:7:9:7:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:10:26:11:1 | functio ... es) {\\n} | here | | csurf_api_example.js:39:37:39:50 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_api_example.js:39:53:41:3 | functio ... e')\\n } | here | | csurf_example.js:18:9:18:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_example.js:29:40:31:1 | functio ... sed')\\n} | here | +| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:23:42:25:1 | functio ... sed')\\n} | here | +| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:27:55:29:1 | functio ... sed')\\n} | here | +| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:31:40:33:1 | functio ... sed')\\n} | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js b/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js new file mode 100644 index 000000000000..51d963ed6afe --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js @@ -0,0 +1,33 @@ +var express = require('express') +var cookieParser = require('cookie-parser') +var bodyParser = require('body-parser') + +var parseForm = bodyParser.urlencoded({ extended: false }) +var lusca = require('lusca'); + +var app = express() +app.use(cookieParser()) + +app.post('/process', parseForm, lusca.csrf(), function (req, res) { // OK + res.send('data is being processed') +}) + +app.post('/process', parseForm, lusca({csrf:true}), function (req, res) { // OK + res.send('data is being processed') +}) + +app.post('/process', parseForm, lusca({csrf:{}}), function (req, res) { // OK + res.send('data is being processed') +}) + +app.post('/process', parseForm, lusca(), function (req, res) { // NOT OK - missing csrf option + res.send('data is being processed') +}) + +app.post('/process', parseForm, lusca({csrf: false}), function (req, res) { // NOT OK - csrf disabled + res.send('data is being processed') +}) + +app.post('/process_unsafe', parseForm, function (req, res) { // NOT OK + res.send('data is being processed') +}) From d2a04d32be953f2806abca0279e632978c8ac6e6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Sep 2018 13:20:02 +0100 Subject: [PATCH 082/110] JS: add change note --- change-notes/1.19/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 4415af95d30c..d86866c78d3a 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -22,5 +22,6 @@ | Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. | | Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. | | Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. | +| Missing CSRF middleware | Fewer false-positive results | This rule now recognizes CSRF middleware from the [lusca](https://www.npmjs.com/package/lusca) package. | ## Changes to QL libraries From 4797924bea1731b637a9b8c78f646e5f4fe9036f Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 21 Sep 2018 14:41:00 +0100 Subject: [PATCH 083/110] JS: review comments --- change-notes/1.19/analysis-javascript.md | 2 +- javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql | 2 +- .../Security/CWE-352/MissingCsrfMiddleware.expected | 3 +-- .../ql/test/query-tests/Security/CWE-352/lusca_example.js | 4 ---- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index d86866c78d3a..82077d8a8e46 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -22,6 +22,6 @@ | Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. | | Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. | | Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. | -| Missing CSRF middleware | Fewer false-positive results | This rule now recognizes CSRF middleware from the [lusca](https://www.npmjs.com/package/lusca) package. | +| Missing CSRF middleware | Fewer false-positive results | This rule now recognizes additional CSRF protection middlewares. | ## Changes to QL libraries diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 554f0f310958..9a1a3e1aa883 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -44,7 +44,7 @@ DataFlow::CallNode csrfMiddlewareCreation() { callee = DataFlow::moduleImport("csurf") or callee = DataFlow::moduleImport("lusca") and - result.getOptionArgument(0, "csrf").analyze().getABooleanValue() = true // any truthy value will enable CSRF + exists(result.getOptionArgument(0, "csrf")) or callee = DataFlow::moduleMember("lusca", "csrf") ) diff --git a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected index dcdbcc9d4b5c..6e063b6454b6 100644 --- a/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected +++ b/javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected @@ -2,5 +2,4 @@ | csurf_api_example.js:39:37:39:50 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_api_example.js:39:53:41:3 | functio ... e')\\n } | here | | csurf_example.js:18:9:18:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_example.js:29:40:31:1 | functio ... sed')\\n} | here | | lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:23:42:25:1 | functio ... sed')\\n} | here | -| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:27:55:29:1 | functio ... sed')\\n} | here | -| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:31:40:33:1 | functio ... sed')\\n} | here | +| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:27:40:29:1 | functio ... sed')\\n} | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js b/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js index 51d963ed6afe..ee91d060dd1a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js +++ b/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js @@ -24,10 +24,6 @@ app.post('/process', parseForm, lusca(), function (req, res) { // NOT OK - missi res.send('data is being processed') }) -app.post('/process', parseForm, lusca({csrf: false}), function (req, res) { // NOT OK - csrf disabled - res.send('data is being processed') -}) - app.post('/process_unsafe', parseForm, function (req, res) { // NOT OK res.send('data is being processed') }) From d5a48ad63e8462df70bb4471455de1cb40896a2c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 21 Sep 2018 15:45:21 +0100 Subject: [PATCH 084/110] CPP: Additional test cases. --- .../AV Rule 79/AV Rule 79.expected | 4 +- .../jsf/4.10 Classes/AV Rule 79/Variants.cpp | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected index 2aad28d2c4eb..6c8715afb56e 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected @@ -13,4 +13,6 @@ | NoDestructor.cpp:23:3:23:20 | ... = ... | Resource n is acquired by class MyClass5 but not released anywhere in this class. | | PlacementNew.cpp:36:3:36:36 | ... = ... | Resource p1 is acquired by class MyTestForPlacementNew but not released anywhere in this class. | | SelfRegistering.cpp:25:3:25:24 | ... = ... | Resource side is acquired by class MyOwner but not released anywhere in this class. | -| Variants.cpp:23:3:23:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. | +| Variants.cpp:25:3:25:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. | +| Variants.cpp:65:3:65:17 | ... = ... | Resource a is acquired by class MyClass6 but not released anywhere in this class. | +| Variants.cpp:66:3:66:36 | ... = ... | Resource b is acquired by class MyClass6 but not released anywhere in this class. | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp index bbbfab1fc922..5f4cc89da54c 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp @@ -2,6 +2,8 @@ // library typedef unsigned int size_t; void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); void free(void* ptr); int *ID(int *x) @@ -34,3 +36,40 @@ class MyClass4 int *a, *b, *c, *d, *e, *f, *g; }; + +class MyClass5 +{ +public: + MyClass5() + { + a = new int[10]; // GOOD + b = (int *)calloc(10, sizeof(int)); // GOOD + c = (int *)realloc(0, 10 * sizeof(int)); // GOOD + } + + ~MyClass5() + { + delete [] a; + free(b); + free(c); + } + + int *a, *b, *c; +}; + +class MyClass6 +{ +public: + MyClass6() + { + a = new int[10]; // BAD + b = (int *)calloc(10, sizeof(int)); // BAD + c = (int *)realloc(0, 10 * sizeof(int)); // BAD [NOT DETECTED] + } + + ~MyClass6() + { + } + + int *a, *b, *c; +}; From 783be15a45939dd04c825dc723b40ac9e2494730 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 21 Sep 2018 11:14:14 -0700 Subject: [PATCH 085/110] Fixing typos & ID. NOTE: There is an ongoing discussion on the proper CWE we should use --- cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql | 2 +- .../Security/CWE/CWE-704/IncorrectTypeConversion.cpp | 2 +- .../Security/CWE/CWE-704/IncorrectTypeConversion.qlref | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql index 900ee6ace2fc..0ff7502d9a90 100644 --- a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql +++ b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql @@ -4,7 +4,7 @@ * Boolean types indicate success by a non-zero value, whereas success (S_OK) in HRESULT is indicated by a value of 0. * Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result. * @kind problem - * @id cpp/incorrect-type-conversion + * @id cpp/hresult-to-boolean-conversion * @problem.severity error * @precision high * @tags security diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp index cb29d8cd742f..8ecab54b4df5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp @@ -65,7 +65,7 @@ void IncorrectTypeConversionTest() { } hr = S_FALSE; - if (hr) // Should fail + if (hr) // BUG { // ... } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref index cd59f148bd02..56b47b192d7a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref @@ -1 +1 @@ -Security/CWE/CWE-704/incorrectTypeConversion.ql \ No newline at end of file +Security/CWE/CWE-704/IncorrectTypeConversion.ql \ No newline at end of file From 75ef377ac1b14483186f58bd6fdc2c7d34535097 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 21 Sep 2018 11:34:22 -0700 Subject: [PATCH 086/110] Replace Unicode apostrophe with ANSI single quote --- .gitignore | 3 +++ .../Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql | 8 ++++---- .../CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 4b055e55a091..effd82ac428a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ /.vs/ql/v15/Browse.VC.opendb /.vs/ql/v15/Browse.VC.db /.vs/ProjectSettings.json +/.vs/ql5/v15/Browse.VC.opendb +/.vs/ql5/v15/Browse.VC.db +/.vs/ql5/v15/.suo diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql index 19551a46c5be..a7d17f4de329 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -1,6 +1,6 @@ /** - * @name Setting a SECURITY_DESCRIPTOR’s DACL to NULL - * @description Setting a SECURITY_DESCRIPTOR’s DACL to NULL will result in an unprotected object. + * @name Setting a SECURITY_DESCRIPTOR's DACL to NULL + * @description Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. * If the DACL that belongs to the security descriptor of an object is set to NULL, a null DACL is created. * A null DACL grants full access to any user who requests it; * normal security checking is not performed with respect to the object. @@ -49,12 +49,12 @@ class SetSecurityDescriptorDaclFunctionConfiguration extends DataFlow::Configura from SetSecurityDescriptorDaclFunctionCall call, string message where exists( NullValue nullExpr | - message = "Setting a SECURITY_DESCRIPTOR’s DACL to NULL will result in an unprotected object." | + message = "Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object." | call.getArgument(1).getValue().toInt() != 0 and call.getArgument(2) = nullExpr ) or exists( Expr constassign, VariableAccess var, SetSecurityDescriptorDaclFunctionConfiguration config | - message = "Setting a SECURITY_DESCRIPTOR’s DACL using variable " + var + " that is set to NULL will result in an unprotected object." | + message = "Setting a SECURITY_DESCRIPTOR's DACL using variable " + var + " that is set to NULL will result in an unprotected object." | var = call.getArgument(2) and config.hasFlow(DataFlow::exprNode(constassign), DataFlow::exprNode(var)) ) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected index dd4b1db43c11..b759bd907d83 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected @@ -1,2 +1,2 @@ -| UnsafeDaclSecurityDescriptor.cpp:69:6:69:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL to NULL will result in an unprotected object. | -| UnsafeDaclSecurityDescriptor.cpp:75:6:75:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL using variable pDacl that is set to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:69:6:69:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:75:6:75:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR's DACL using variable pDacl that is set to NULL will result in an unprotected object. | From 3922082e7dbb02e109ffb4d94d1fa2c1eb4c65ba Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 21 Sep 2018 13:08:49 +0100 Subject: [PATCH 087/110] CPP: Tidy and simplify AV Rule 79.ql. --- cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql | 147 +++++++++--------- .../AV Rule 79/AV Rule 79.expected | 1 + .../jsf/4.10 Classes/AV Rule 79/Variants.cpp | 2 +- 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql index 3e9c6b1cccf7..853285050a42 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql @@ -10,33 +10,69 @@ * external/cwe/cwe-404 */ import cpp +import Critical.NewDelete -// List pairs of functions that do resource acquisition/release -// Extend this to add custom function pairs. As written the query -// will only apply if the resource is the *return value* of the -// first call and a *parameter* to the second. Other cases should -// be handled differently. -predicate resourceManagementPair(string acquire, string release) { - - (acquire = "fopen" and release = "fclose") - or - (acquire = "open" and release = "close") - or - (acquire = "socket" and release = "close") - -} - -// List functions that return malloc-allocated memory. Customize -// to list your own functions there -predicate mallocFunction(Function malloc) { - malloc.hasName("malloc") or malloc.hasName("calloc") or // Not realloc: doesn't acquire it, really - malloc.hasName("strdup") +/** + * An expression that acquires a resource, and the kind of resource that is acquired. The + * kind of a resource indicates which acquisition/release expressions can be paired. + */ +predicate acquireExpr(Expr acquire, string kind) { + exists(FunctionCall fc, Function f, string name | + fc = acquire and + f = fc.getTarget() and + name = f.getName() and + ( + ( + name = "fopen" and + kind = "file" + ) or ( + name = "open" and + kind = "file descriptor" + ) or ( + name = "socket" and + kind = "file descriptor" + ) + ) + ) or ( + allocExpr(acquire, kind) + ) } -private predicate isRelease(string release) { - resourceManagementPair(_, release) or - release = "free" or - release = "delete" +/** + * An expression that releases a resource, and the kind of resource that is released. The + * kind of a resource indicates which acquisition/release expressions can be paired. + */ +predicate releaseExpr(Expr release, Expr resource, string kind) { + exists(FunctionCall fc, Function f, string name | + fc = release and + f = fc.getTarget() and + name = f.getName() and + ( + ( + name = "fclose" and + resource = fc.getArgument(0) and + kind = "file" + ) or ( + name = "close" and + resource = fc.getArgument(0) and + kind = "file descriptor" + ) + ) + ) or exists(string releaseKind | + freeExpr(release, resource, releaseKind) and + ( + ( + kind = "malloc" and + releaseKind = "free" + ) or ( + kind = "new" and + releaseKind = "delete" + ) or ( + kind = "new[]" and + releaseKind = "delete[]" + ) + ) + ) } /** @@ -52,35 +88,23 @@ Expr exprOrDereference(Expr e) { * Holds if the expression `e` releases expression `released`, whether directly * or via one or more function call(s). */ -private predicate exprReleases(Expr e, Expr released, string releaseType) { +private predicate exprReleases(Expr e, Expr released, string kind) { ( - // `e` is a call to a release function and `released` is any argument - e.(FunctionCall).getTarget().getName() = releaseType and - isRelease(releaseType) and - e.(FunctionCall).getAnArgument() = released - ) or ( - // `e` is a call to `delete` and `released` is the target - e.(DeleteExpr).getExpr() = released and - releaseType = "delete" - ) or ( - // `e` is a call to `delete[]` and `released` is the target - e.(DeleteArrayExpr).getExpr() = released and - releaseType = "delete" + // `e` is a call to a release function and `released` is the released argument + releaseExpr(e, released, kind) ) or exists(Function f, int arg | // `e` is a call to a function that releases one of it's parameters, // and `released` is the corresponding argument e.(FunctionCall).getTarget() = f and e.(FunctionCall).getArgument(arg) = released and - exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), releaseType) - ) or exists(Function f, Expr innerThis | + exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), kind) + ) or exists(Function f, ThisExpr innerThis | // `e` is a call to a method that releases `this`, and `released` // is the object that is called e.(FunctionCall).getTarget() = f and e.(FunctionCall).getQualifier() = exprOrDereference(released) and innerThis.getEnclosingFunction() = f and - exprReleases(_, innerThis, releaseType) and - innerThis instanceof ThisExpr and - releaseType = "delete" + exprReleases(_, innerThis, kind) ) } @@ -109,42 +133,17 @@ class Resource extends MemberVariable { ) } - predicate acquisitionWithRequiredRelease(Expr acquire, string releaseName) { - acquire.(Assignment).getLValue() = this.getAnAccess() and + predicate acquisitionWithRequiredRelease(Assignment acquireAssign, string kind) { + // acquireAssign is an assignment to this resource + acquireAssign.(Assignment).getLValue() = this.getAnAccess() and // Should be in this class, but *any* member method will do - this.inSameClass(acquire) and + this.inSameClass(acquireAssign) and // Check that it is an acquisition function and return the corresponding free - ( - exists(Function f | f = acquire.(Assignment).getRValue().(FunctionCall).getTarget() and - (resourceManagementPair(f.getName(), releaseName) or (mallocFunction(f) and (releaseName = "free" or releaseName = "delete"))) - ) - or - (acquire = this.getANew() and releaseName = "delete") - ) - } - - private Assignment getANew() { - result.getLValue() = this.getAnAccess() and - ( - ( - result.getRValue() instanceof NewExpr and - - // exclude placement new and custom overloads as they - // may not conform to assumptions - not result.getRValue().(NewExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 - ) or ( - result.getRValue() instanceof NewArrayExpr and - - // exclude placement new and custom overloads as they - // may not conform to assumptions - not result.getRValue().(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 - ) - ) and - this.inSameClass(result) + acquireExpr(acquireAssign.getRValue(), kind) } - Expr getAReleaseExpr(string releaseName) { - exprReleases(result, this.getAnAccess(), releaseName) + Expr getAReleaseExpr(string kind) { + exprReleases(result, this.getAnAccess(), kind) } } diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected index 6c8715afb56e..eed2f14a99fe 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected @@ -16,3 +16,4 @@ | Variants.cpp:25:3:25:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. | | Variants.cpp:65:3:65:17 | ... = ... | Resource a is acquired by class MyClass6 but not released anywhere in this class. | | Variants.cpp:66:3:66:36 | ... = ... | Resource b is acquired by class MyClass6 but not released anywhere in this class. | +| Variants.cpp:67:3:67:41 | ... = ... | Resource c is acquired by class MyClass6 but not released anywhere in this class. | diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp index 5f4cc89da54c..dbeee230de4f 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp @@ -64,7 +64,7 @@ class MyClass6 { a = new int[10]; // BAD b = (int *)calloc(10, sizeof(int)); // BAD - c = (int *)realloc(0, 10 * sizeof(int)); // BAD [NOT DETECTED] + c = (int *)realloc(0, 10 * sizeof(int)); // BAD } ~MyClass6() From c22787293e225a2bbda375b4e8a54779866399a2 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Fri, 21 Sep 2018 11:35:43 -0700 Subject: [PATCH 088/110] Update .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index effd82ac428a..4b055e55a091 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,3 @@ /.vs/ql/v15/Browse.VC.opendb /.vs/ql/v15/Browse.VC.db /.vs/ProjectSettings.json -/.vs/ql5/v15/Browse.VC.opendb -/.vs/ql5/v15/Browse.VC.db -/.vs/ql5/v15/.suo From 8519f1a9e19d303b0ba9da9b83b1b2416984c355 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 21 Sep 2018 13:07:39 -0700 Subject: [PATCH 089/110] Fixing tabs replaced to spaces --- .../CWE-732/UnsafeDaclSecurityDescriptor.cpp | 12 +-- .../CWE-732/UnsafeDaclSecurityDescriptor.cpp | 102 +++++++++--------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp index 590f1482e8f6..1324185481f4 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -3,12 +3,12 @@ SECURITY_ATTRIBUTES SA; if (!InitializeSecurityDescriptor(&pSD, SECURITY_DESCRIPTOR_REVISION)) { - // error handling + // error handling } -if (!SetSecurityDescriptorDacl(&pSD, - TRUE, // bDaclPresent - this value indicates the presence of a DACL in the security descriptor - NULL, // pDacl - the pDacl parameter does not point to a DACL. All access will be allowed - FALSE)) +if (!SetSecurityDescriptorDacl(&pSD, + TRUE, // bDaclPresent - this value indicates the presence of a DACL in the security descriptor + NULL, // pDacl - the pDacl parameter does not point to a DACL. All access will be allowed + FALSE)) { - // error handling + // error handling } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp index 9fe34cf5d5cc..62de434be970 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -1,91 +1,91 @@ typedef unsigned long DWORD; -typedef unsigned long ULONG; +typedef unsigned long ULONG; typedef unsigned char BYTE; typedef unsigned short WORD; typedef int BOOL; -typedef void *PVOID; +typedef void *PVOID; #define TRUE 1 -#define FALSE 0 +#define FALSE 0 #define ERROR_SUCCESS 0L -#define NULL 0 +#define NULL 0 typedef PVOID PSECURITY_DESCRIPTOR; typedef struct _ACL { - BYTE AclRevision; - BYTE Sbz1; - WORD AclSize; - WORD AceCount; - WORD Sbz2; + BYTE AclRevision; + BYTE Sbz1; + WORD AclSize; + WORD AceCount; + WORD Sbz2; } ACL; typedef ACL *PACL; typedef enum _ACCESS_MODE { - NOT_USED_ACCESS = 0, - GRANT_ACCESS, - SET_ACCESS, - DENY_ACCESS, - REVOKE_ACCESS, - SET_AUDIT_SUCCESS, - SET_AUDIT_FAILURE + NOT_USED_ACCESS = 0, + GRANT_ACCESS, + SET_ACCESS, + DENY_ACCESS, + REVOKE_ACCESS, + SET_AUDIT_SUCCESS, + SET_AUDIT_FAILURE } ACCESS_MODE; typedef int TRUSTEE_W; typedef struct _EXPLICIT_ACCESS_W { - DWORD grfAccessPermissions; - ACCESS_MODE grfAccessMode; - DWORD grfInheritance; - TRUSTEE_W Trustee; + DWORD grfAccessPermissions; + ACCESS_MODE grfAccessMode; + DWORD grfInheritance; + TRUSTEE_W Trustee; } EXPLICIT_ACCESS_W, *PEXPLICIT_ACCESS_W, EXPLICIT_ACCESSW, *PEXPLICIT_ACCESSW; BOOL SetSecurityDescriptorDacl( - PSECURITY_DESCRIPTOR pSecurityDescriptor, - BOOL bDaclPresent, - PACL pDacl, - BOOL bDaclDefaulted + PSECURITY_DESCRIPTOR pSecurityDescriptor, + BOOL bDaclPresent, + PACL pDacl, + BOOL bDaclDefaulted ) { - return TRUE; + return TRUE; } DWORD SetEntriesInAcl( - ULONG cCountOfExplicitEntries, - PEXPLICIT_ACCESS_W pListOfExplicitEntries, - PACL OldAcl, - PACL *NewAcl + ULONG cCountOfExplicitEntries, + PEXPLICIT_ACCESS_W pListOfExplicitEntries, + PACL OldAcl, + PACL *NewAcl ) { - *NewAcl = (PACL)0xFFFFFF; - return ERROR_SUCCESS; + *NewAcl = (PACL)0xFFFFFF; + return ERROR_SUCCESS; } void Test() { - PSECURITY_DESCRIPTOR pSecurityDescriptor; - BOOL b; - b = SetSecurityDescriptorDacl(pSecurityDescriptor, - TRUE, // Dacl Present - NULL, // NULL pointer to DACL == BUG - FALSE); + PSECURITY_DESCRIPTOR pSecurityDescriptor; + BOOL b; + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + NULL, // NULL pointer to DACL == BUG + FALSE); - PACL pDacl = NULL; - b = SetSecurityDescriptorDacl(pSecurityDescriptor, - TRUE, // Dacl Present - pDacl, // NULL pointer to DACL == BUG - FALSE); + PACL pDacl = NULL; + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + pDacl, // NULL pointer to DACL == BUG + FALSE); - SetEntriesInAcl(0, NULL, NULL, &pDacl); - b = SetSecurityDescriptorDacl(pSecurityDescriptor, - TRUE, // Dacl Present - pDacl, // Should have been set by SetEntriesInAcl ==> should not be flagged - FALSE); + SetEntriesInAcl(0, NULL, NULL, &pDacl); + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + TRUE, // Dacl Present + pDacl, // Should have been set by SetEntriesInAcl ==> should not be flagged + FALSE); - b = SetSecurityDescriptorDacl(pSecurityDescriptor, - FALSE, // Dacl is not Present - NULL, // DACL is going to be removed from security descriptor. Default/inherited access ==> should not be flagged - FALSE); + b = SetSecurityDescriptorDacl(pSecurityDescriptor, + FALSE, // Dacl is not Present + NULL, // DACL is going to be removed from security descriptor. Default/inherited access ==> should not be flagged + FALSE); } \ No newline at end of file From 492d79ea53cc972983fb8316e24a11247e104e70 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 21 Sep 2018 21:13:37 +0100 Subject: [PATCH 090/110] CPP: Change note. --- change-notes/1.19/analysis-cpp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.19/analysis-cpp.md b/change-notes/1.19/analysis-cpp.md index 17a165e5928a..7fcf1fd234c0 100644 --- a/change-notes/1.19/analysis-cpp.md +++ b/change-notes/1.19/analysis-cpp.md @@ -12,7 +12,7 @@ | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------------------------------------------------------------| -| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* | +| Resource not released in destructor | Fewer false positive results | Placement new is now excluded from the query. | ## Changes to QL libraries From 925c3b51f9a601c6d1e84ed617bf8e158d8a863c Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 21 Sep 2018 15:21:07 -0700 Subject: [PATCH 091/110] Adding semmle-extractor-options: --microsoft to test --- .gitignore | 3 +++ .../Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4b055e55a091..effd82ac428a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ /.vs/ql/v15/Browse.VC.opendb /.vs/ql/v15/Browse.VC.db /.vs/ProjectSettings.json +/.vs/ql5/v15/Browse.VC.opendb +/.vs/ql5/v15/Browse.VC.db +/.vs/ql5/v15/.suo diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp index 62de434be970..8c5e0e632d84 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -1,3 +1,4 @@ +// semmle-extractor-options: --microsoft typedef unsigned long DWORD; typedef unsigned long ULONG; typedef unsigned char BYTE; From 242ee108064707f05250b15667f719ca286fa109 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 21 Sep 2018 16:47:31 -0700 Subject: [PATCH 092/110] Major change in order to support the rule for C as well as cpp --- .gitignore | 3 + .../HResultBooleanConversion.cpp} | 2 +- .../HResultBooleanConversion.qhelp} | 0 .../CWE/CWE-253/HResultBooleanConversion.ql | 71 +++++++++++++ .../CWE/CWE-704/IncorrectTypeConversion.ql | 30 ------ .../CWE/CWE-253/HResultBooleanConversion.c | 100 ++++++++++++++++++ .../CWE/CWE-253/HResultBooleanConversion.cpp | 97 +++++++++++++++++ .../CWE-253/HResultBooleanConversion.expected | 20 ++++ .../CWE-253/HResultBooleanConversion.qlref | 1 + .../CWE/CWE-704/IncorrectTypeConversion.cpp | 76 ------------- .../CWE-704/IncorrectTypeConversion.expected | 6 -- .../CWE/CWE-704/IncorrectTypeConversion.qlref | 1 - 12 files changed, 293 insertions(+), 114 deletions(-) rename cpp/ql/src/Security/CWE/{CWE-704/IncorrectTypeConversion.cpp => CWE-253/HResultBooleanConversion.cpp} (81%) rename cpp/ql/src/Security/CWE/{CWE-704/IncorrectTypeConversion.qhelp => CWE-253/HResultBooleanConversion.qhelp} (100%) create mode 100644 cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql delete mode 100644 cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.c create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.cpp create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.expected create mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.qlref delete mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp delete mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected delete mode 100644 cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref diff --git a/.gitignore b/.gitignore index 4b055e55a091..fdcd174a97c6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ /.vs/ql/v15/Browse.VC.opendb /.vs/ql/v15/Browse.VC.db /.vs/ProjectSettings.json +/.vs/slnx.sqlite-journal +/.vs/ql2/v15/Browse.VC.opendb +/.vs/ql2/v15/Browse.VC.db diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.cpp similarity index 81% rename from cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp rename to cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.cpp index bf711c9b2886..44896e89e153 100644 --- a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.cpp +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.cpp @@ -3,5 +3,5 @@ HRESULT hr = CoGetMalloc(1, &pMalloc); if (!hr) { - // code ... + // code ... } diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp similarity index 100% rename from cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.qhelp rename to cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql new file mode 100644 index 000000000000..7678cff23f0f --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql @@ -0,0 +1,71 @@ +/** + * @name Cast between semantically different integer types: HRESULT to/from a Boolean type + * @description Cast between semantically different integer types: HRESULT to/from a Boolean type. + * Boolean types indicate success by a non-zero value, whereas success (S_OK) in HRESULT is indicated by a value of 0. + * Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result. + * @kind problem + * @id cpp/hresult-boolean-conversion + * @problem.severity error + * @precision high + * @tags security + * external/cwe/cwe-253 + * external/microsoft/C6214 + * external/microsoft/C6215 + * external/microsoft/C6216 + * external/microsoft/C6217 + * external/microsoft/C6230 + */ +import cpp + +predicate isHresultBooleanConverted( Expr e1, Cast e2 ) +{ + exists ( Type t1, Type t2 | + t1 = e1.getType() and + t2 = e2.getType() and + ((t1.hasName("bool") or t1.hasName("BOOL") or t1.hasName("_Bool")) and t2.hasName("HRESULT") or + (t2.hasName("bool") or t2.hasName("BOOL") or t2.hasName("_Bool")) and t1.hasName("HRESULT") + )) +} + +predicate isHresultBooleanConverted( Expr e1 ) +{ + exists( Cast e2 | + e2 = e1.getConversion() and + isHresultBooleanConverted(e1, e2) + ) +} + +from Expr e1, string msg +where exists + ( + Cast e2 | + e2 = e1.getConversion() | + isHresultBooleanConverted( e1, e2 ) + and if e2.isImplicit() then ( msg = "Implicit conversion from " + e1.getType().toString() + " to " + e2.getType().toString()) + else ( msg = "Explicit conversion from " + e1.getType().toString() + " to " + e2.getType().toString()) + ) + or exists + ( + ControlStructure ctls | + ctls.getControllingExpr() = e1 + and e1.getType().(TypedefType).hasName("HRESULT") + and not isHresultBooleanConverted(e1) + and msg = "Direct usage of a type " + e1.getType().toString() + " as a conditional expression" + ) + or + ( + exists( BinaryLogicalOperation blop | + blop.getAnOperand() = e1 | + e1.getType().(TypedefType).hasName("HRESULT") + and msg = "Usage of a type " + e1.getType().toString() + " as an argument of a binary logical operation" + ) + or exists + ( + UnaryLogicalOperation ulop | + ulop.getAnOperand() = e1 | + e1.getType().(TypedefType).hasName("HRESULT") + and msg = "Usage of a type " + e1.getType().toString() + " as an argument of a unary logical operation" + ) + and not isHresultBooleanConverted(e1) + ) +select e1, msg \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql deleted file mode 100644 index 0ff7502d9a90..000000000000 --- a/cpp/ql/src/Security/CWE/CWE-704/IncorrectTypeConversion.ql +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @name Cast between semantically different integer types: HRESULT to/from a Boolean type - * @description Cast between semantically different integer types: HRESULT to/from a Boolean type. - * Boolean types indicate success by a non-zero value, whereas success (S_OK) in HRESULT is indicated by a value of 0. - * Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result. - * @kind problem - * @id cpp/hresult-to-boolean-conversion - * @problem.severity error - * @precision high - * @tags security - * external/cwe/cwe-704 - * external/microsoft/C6214 - * external/microsoft/C6215 - * external/microsoft/C6216 - * external/microsoft/C6217 - * external/microsoft/C6230 - */ -import cpp - -from Expr e1, Cast e2, string msg -where e2 = e1.getConversion() and - exists ( Type t1, Type t2 | - t1 = e1.getType() and - t2 = e2.getType() and - ((t1.hasName("bool") or t1.hasName("BOOL")) and t2.hasName("HRESULT") or - (t2.hasName("bool") or t2.hasName("BOOL")) and t1.hasName("HRESULT") - )) - and if e2.isImplicit() then ( msg = "Implicit" ) - else ( msg = "Explicit" ) -select e1, msg + " conversion from " + e1.getType().toString() + " to " + e2.getType().toString() \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.c b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.c new file mode 100644 index 000000000000..9edcd34a8df0 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.c @@ -0,0 +1,100 @@ +// semmle-extractor-options: --microsoft +// winnt.h +typedef long HRESULT; +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) + +typedef _Bool bool; +#define FALSE 0 + +// minwindef.h +typedef int BOOL; +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +// winerror.h +#define S_OK ((HRESULT)0L) +#define S_FALSE ((HRESULT)1L) +#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL) + +HRESULT HresultFunction() +{ + return S_OK; +} + +BOOL BoolFunction() +{ + return FALSE; +} + +bool BoolFunction2() +{ + return FALSE; +} + +HRESULT IncorrectHresultFunction() +{ + return BoolFunction(); // BUG +} + +HRESULT IncorrectHresultFunction2() +{ + return BoolFunction2(); // BUG +} + +void IncorrectTypeConversionTest() { + + HRESULT hr = HresultFunction(); + if ((BOOL)hr) // BUG + { + // ... + } + if ((bool)hr) // BUG + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } + + if (SUCCEEDED(BoolFunction())) // BUG + { + // ... + } + if (SUCCEEDED(BoolFunction2())) // BUG + { + // ... + } + if (BoolFunction()) // Correct Usage + { + // ... + } + BOOL b = IncorrectHresultFunction(); // BUG + bool b2 = IncorrectHresultFunction(); // BUG + + hr = E_UNEXPECTED; + if (!hr) // BUG + { + // ... + } + if (!FAILED(hr)) // Correct Usage + { + // ... + } + + hr = S_FALSE; + if (hr) // BUG + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.cpp new file mode 100644 index 000000000000..04588c24264e --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.cpp @@ -0,0 +1,97 @@ +// semmle-extractor-options: --microsoft +// winnt.h +typedef long HRESULT; +#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) +#define FAILED(hr) (((HRESULT)(hr)) < 0) + +// minwindef.h +typedef int BOOL; +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +// winerror.h +#define S_OK ((HRESULT)0L) +#define S_FALSE ((HRESULT)1L) +#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) +#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL) + +HRESULT HresultFunction() +{ + return S_OK; +} + +BOOL BoolFunction() +{ + return FALSE; +} + +bool BoolFunction2() +{ + return FALSE; +} + +HRESULT IncorrectHresultFunction() +{ + return BoolFunction(); // BUG +} + +HRESULT IncorrectHresultFunction2() +{ + return BoolFunction2(); // BUG +} + +void IncorrectTypeConversionTest() { + + HRESULT hr = HresultFunction(); + if ((BOOL)hr) // BUG + { + // ... + } + if ((bool)hr) // BUG + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } + + if (SUCCEEDED(BoolFunction())) // BUG + { + // ... + } + if (SUCCEEDED(BoolFunction2())) // BUG + { + // ... + } + if (BoolFunction()) // Correct Usage + { + // ... + } + BOOL b = IncorrectHresultFunction(); // BUG + bool b2 = IncorrectHresultFunction(); // BUG + + hr = E_UNEXPECTED; + if (!hr) // BUG + { + // ... + } + if (!FAILED(hr)) // Correct Usage + { + // ... + } + + hr = S_FALSE; + if (hr) // BUG + { + // ... + } + if (SUCCEEDED(hr)) // Correct Usage + { + // ... + } +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.expected new file mode 100644 index 000000000000..15996702920f --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.expected @@ -0,0 +1,20 @@ +| HResultBooleanConversion.c:42:12:42:23 | call to BoolFunction | Implicit conversion from BOOL to HRESULT | +| HResultBooleanConversion.c:47:12:47:24 | call to BoolFunction2 | Implicit conversion from bool to HRESULT | +| HResultBooleanConversion.c:53:15:53:16 | hr | Explicit conversion from HRESULT to BOOL | +| HResultBooleanConversion.c:57:15:57:16 | hr | Explicit conversion from HRESULT to bool | +| HResultBooleanConversion.c:66:9:66:33 | (...) | Explicit conversion from BOOL to HRESULT | +| HResultBooleanConversion.c:70:9:70:34 | (...) | Explicit conversion from bool to HRESULT | +| HResultBooleanConversion.c:78:14:78:37 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to BOOL | +| HResultBooleanConversion.c:79:15:79:38 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to bool | +| HResultBooleanConversion.c:82:10:82:11 | hr | Usage of a type HRESULT as an argument of a unary logical operation | +| HResultBooleanConversion.c:92:9:92:10 | hr | Direct usage of a type HRESULT as a conditional expression | +| HResultBooleanConversion.cpp:39:12:39:23 | call to BoolFunction | Implicit conversion from BOOL to HRESULT | +| HResultBooleanConversion.cpp:44:12:44:24 | call to BoolFunction2 | Implicit conversion from bool to HRESULT | +| HResultBooleanConversion.cpp:50:15:50:16 | hr | Explicit conversion from HRESULT to BOOL | +| HResultBooleanConversion.cpp:54:15:54:16 | hr | Explicit conversion from HRESULT to bool | +| HResultBooleanConversion.cpp:63:9:63:33 | (...) | Explicit conversion from BOOL to HRESULT | +| HResultBooleanConversion.cpp:67:9:67:34 | (...) | Explicit conversion from bool to HRESULT | +| HResultBooleanConversion.cpp:75:14:75:37 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to BOOL | +| HResultBooleanConversion.cpp:76:15:76:38 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to bool | +| HResultBooleanConversion.cpp:79:10:79:11 | hr | Implicit conversion from HRESULT to bool | +| HResultBooleanConversion.cpp:89:9:89:10 | hr | Implicit conversion from HRESULT to bool | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.qlref new file mode 100644 index 000000000000..a345e5c6dfb3 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-253/HResultBooleanConversion.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-253/HResultBooleanConversion.ql \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp deleted file mode 100644 index 8ecab54b4df5..000000000000 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// winnt.h -typedef long HRESULT; -#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) -#define FAILED(hr) (((HRESULT)(hr)) < 0) - -// minwindef.h -typedef int BOOL; -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE 1 -#endif - -// winerror.h -#define S_OK ((HRESULT)0L) -#define S_FALSE ((HRESULT)1L) -#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc) -#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL) - -HRESULT HresultFunction() -{ - return S_OK; -} - -BOOL BoolFunction() -{ - return FALSE; -} - -HRESULT IncorrectHresultFunction() -{ - return BoolFunction(); // BUG -} - -void IncorrectTypeConversionTest() { - HRESULT hr = HresultFunction(); - if ((BOOL)hr) // BUG - { - // ... - } - if (SUCCEEDED(hr)) // Correct Usage - { - // ... - } - - if (SUCCEEDED(BoolFunction())) // BUG - { - // ... - } - if (BoolFunction()) // Correct Usage - { - // ... - } - BOOL b = IncorrectHresultFunction(); // BUG - - hr = E_UNEXPECTED; - if (!hr) // BUG - { - // ... - } - if (!FAILED(hr)) // Correct Usage - { - // ... - } - - hr = S_FALSE; - if (hr) // BUG - { - // ... - } - if (SUCCEEDED(hr)) // Correct Usage - { - // ... - } -} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected deleted file mode 100644 index 35c40158a771..000000000000 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.expected +++ /dev/null @@ -1,6 +0,0 @@ -| IncorrectTypeConversion.cpp:33:9:33:20 | call to BoolFunction | Implicit conversion from BOOL to HRESULT | -| IncorrectTypeConversion.cpp:38:12:38:13 | hr | Explicit conversion from HRESULT to BOOL | -| IncorrectTypeConversion.cpp:47:6:47:30 | (...) | Explicit conversion from BOOL to HRESULT | -| IncorrectTypeConversion.cpp:55:11:55:34 | call to IncorrectHresultFunction | Implicit conversion from HRESULT to BOOL | -| IncorrectTypeConversion.cpp:58:7:58:8 | hr | Implicit conversion from HRESULT to bool | -| IncorrectTypeConversion.cpp:68:6:68:7 | hr | Implicit conversion from HRESULT to bool | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref deleted file mode 100644 index 56b47b192d7a..000000000000 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-704/IncorrectTypeConversion.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE/CWE-704/IncorrectTypeConversion.ql \ No newline at end of file From aa267c83027a8216cfaf4e4b7fbe33e5c9ebcc02 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 20 Sep 2018 11:38:56 -0700 Subject: [PATCH 093/110] C++: Force LF for .c,.cpp,.h,.hpp --- .editorconfig | 2 +- .gitattributes | 4 + .../ClassesWithManyDependencies.cpp | 34 +- .../ClassesWithManyFields.cpp | 40 +- .../FunctionsWithManyParameters.cpp | 16 +- .../src/Best Practices/ComplexCondition.cpp | 24 +- .../Hiding/DeclarationHidesParameter.cpp | 12 +- .../Hiding/DeclarationHidesVariable.cpp | 24 +- .../LocalVariableHidesGlobalVariable.cpp | 24 +- .../Likely Errors/EmptyBlock.cpp | 18 +- .../Best Practices/Likely Errors/Slicing.cpp | 86 +- .../Magic Constants/MagicConstantsNumbers.cpp | 32 +- cpp/ql/src/Best Practices/RuleOfTwo.cpp | 52 +- cpp/ql/src/Best Practices/SwitchLongCase.cpp | 62 +- .../Unused Entities/UnusedLocals.cpp | 10 +- .../Unused Entities/UnusedStaticFunctions.cpp | 28 +- .../Unused Entities/UnusedStaticVariables.cpp | 10 +- cpp/ql/src/Critical/DeadCodeCondition.cpp | 38 +- cpp/ql/src/Critical/DeadCodeFunction.cpp | 24 +- .../src/Critical/DescriptorMayNotBeClosed.cpp | 18 +- cpp/ql/src/Critical/DescriptorNeverClosed.cpp | 12 +- cpp/ql/src/Critical/GlobalUseBeforeInit.cpp | 22 +- cpp/ql/src/Critical/InitialisationNotRun.cpp | 24 +- cpp/ql/src/Critical/LargeParameter.cpp | 26 +- cpp/ql/src/Critical/MissingNullTest.cpp | 22 +- .../src/Critical/NewArrayDeleteMismatch.cpp | 10 +- .../src/Critical/NewDeleteArrayMismatch.cpp | 10 +- cpp/ql/src/Critical/NewFreeMismatch.cpp | 10 +- cpp/ql/src/Critical/NotInitialised.cpp | 12 +- cpp/ql/src/Critical/OverflowDestination.cpp | 26 +- cpp/ql/src/Critical/ReturnValueIgnored.cpp | 46 +- cpp/ql/src/Critical/SizeCheck.cpp | 20 +- cpp/ql/src/Critical/Unused.cpp | 8 +- cpp/ql/src/Critical/UseAfterFree.cpp | 18 +- cpp/ql/src/Documentation/TodoComments.cpp | 6 +- .../Cleanup-DuplicateIncludeGuard.cpp | 14 +- .../Cleanup-DuplicateIncludeGuard2.cpp | 14 +- .../Arithmetic/ComparisonPrecedence.cpp | 12 +- .../Conversion/ArrayArgSizeMismatch.cpp | 24 +- .../Conversion/ConversionChangesSign.cpp | 8 +- .../Conversion/LossyPointerCast.cpp | 10 +- .../InconsistentCheckReturnNull.cpp | 60 +- .../Likely Typos/BoolValueInBitOp.cpp | 26 +- .../LogicalExprCouldBeSimplified.cpp | 22 +- .../Likely Typos/MissingEnumCaseInSwitch.cpp | 60 +- .../PotentialBufferOverflow.cpp | 24 +- .../SuspiciousCallToStrncat.cpp | 8 +- .../Memory Management/SuspiciousSizeof.cpp | 8 +- cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp | 32 +- .../Likely Bugs/OO/NonVirtualDestructor.cpp | 62 +- .../src/Likely Bugs/OO/ThrowInDestructor.cpp | 38 +- cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp | 26 +- .../CWE/CWE-190/ComparisonWithWiderType.c | 56 +- cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp | 40 +- .../multi-target-includes/common.h | 8 +- .../multi-target-includes/defines_issue.h | 4 +- .../multi-target-includes/main2.cpp | 14 +- .../multi-target-includes/nameclash.h | 4 +- .../multi-target-includes/subdir1/nameclash.h | 4 +- .../multi-target-includes/subdir2/nameclash.h | 4 +- .../library-tests/allocators/allocators.cpp | 222 +- cpp/ql/test/library-tests/calls/calls1.cpp | 16 +- cpp/ql/test/library-tests/calls/calls1.h | 4 +- cpp/ql/test/library-tests/calls/calls2.cpp | 40 +- .../classes/classes/Classes.ms.cpp | 82 +- .../classes/derivations/offsets/offsets.cpp | 168 +- .../loopentrycondition/forstmt.cpp | 202 +- .../loopentrycondition/whilestmt.cpp | 126 +- .../library-tests/conversions/conversions.cpp | 500 ++--- .../library-tests/ctorinits/ctorinits.cpp | 88 +- .../declarationEntry/template/src5.cpp | 20 +- .../declarationEntry/template/src5.fwd.hpp | 16 +- .../declarationEntry/template/src6.cpp | 10 +- .../depends_addressable/addressable.cpp | 56 +- .../library-tests/depends_friends/friends.cpp | 56 +- .../depends_initializers/initializers.cpp | 62 +- .../depends_initializers/template_static.cpp | 178 +- .../template_static_instantiated.cpp | 148 +- .../derived_types/derivedtype.cpp | 18 +- .../test/library-tests/enums/enums/enums.cpp | 30 +- .../exprs/comparison_operation/test.cpp | 28 +- .../exprs/unary_operation/test.cpp | 30 +- .../value_categories/value_categories.cpp | 310 +-- .../exprs_basic/exprs_basic1.cpp | 66 +- .../library-tests/fields/fields/fields.cpp | 66 +- cpp/ql/test/library-tests/files/files1.cpp | 18 +- cpp/ql/test/library-tests/files/files1.h | 4 +- cpp/ql/test/library-tests/files/files2.cpp | 14 +- .../functions/functions/functions.cpp | 66 +- .../ir/constant_func/constant_func.cpp | 64 +- .../test/library-tests/ir/escape/escape.cpp | 196 +- cpp/ql/test/library-tests/ir/ir/ir.cpp | 1968 ++++++++--------- .../aggregate_literals/aggregate_literals.c | 204 +- .../aggregate_literals_cpp.cpp | 56 +- cpp/ql/test/library-tests/loops/loops.cpp | 72 +- .../namespaces/namespaces/namespaces.cpp | 88 +- .../namespaces/same_name/same_name.cpp | 32 +- .../library-tests/operators/operators.cpp | 54 +- .../test/library-tests/padding/size_asserts.h | 92 +- .../parameters/parameters/parameters.cpp | 56 +- .../preprocessor/include/guarded.h | 14 +- .../preprocessor/include/unguarded.h | 6 +- .../preprocessor/linedirective/header.h | 6 +- .../preprocessor/linedirective/line.h | 28 +- .../preprocessor/linedirective/system.h | 28 +- .../library-tests/scopes/scopes/scopes.cpp | 206 +- .../library-tests/sideEffects/exprs/exprs.cpp | 80 +- .../library-tests/specifiers/specifiers.cpp | 44 +- .../string_analysis/string_analysis.cpp | 88 +- .../structs/compatible_variables/a.c | 28 +- .../structs/compatible_variables/b.c | 34 +- .../structs/compatible_variables/h.h | 20 +- .../structs/incomplete_definition/a.h | 12 +- .../structs/incomplete_definition/x.cpp | 62 +- .../structs/incomplete_definition/y.cpp | 16 +- .../structs/mutual_recursion/a.c | 122 +- .../structs/mutual_recursion/b.c | 114 +- .../test/library-tests/typedefs/typedefs.cpp | 62 +- .../integral_types_ms/integral_types.cpp | 10 +- .../types/pointertypes/pointertypes.c | 80 +- .../library-tests/types/refersTo/refersTo.cpp | 40 +- .../test/library-tests/types/scope/scope.cpp | 54 +- .../test/library-tests/types/types/types.cpp | 164 +- .../library-tests/types/wchar_t_typedef/ms.c | 8 +- cpp/ql/test/library-tests/unions/unions.cpp | 46 +- .../GlobalValueNumbering/test.cpp | 6 +- .../variables/variables/variables.cpp | 106 +- .../virtual_functions/virtual_functions.cpp | 96 +- .../AlertSuppression/.gitattributes | 1 + .../query-tests/AlertSuppression/tstWindows.c | 56 +- .../MagicConstantsNumbers/functions.h | 66 +- .../MagicConstantsNumbers/test.cpp | 26 +- .../Unused Entities/UnusedIncludes/a.h | 6 +- .../Unused Entities/UnusedIncludes/b.h | 6 +- .../Unused Entities/UnusedIncludes/c.h | 6 +- .../Unused Entities/UnusedIncludes/d.hpp | 6 +- .../Unused Entities/UnusedIncludes/e.hpp | 6 +- .../Unused Entities/UnusedIncludes/f.fwd.hpp | 6 +- .../Cleanup-DuplicateIncludeGuard/header1.h | 16 +- .../Cleanup-DuplicateIncludeGuard/header2.h | 16 +- .../Cleanup-DuplicateIncludeGuard/header3.h | 24 +- .../Cleanup-DuplicateIncludeGuard/header4.h | 16 +- .../Cleanup-DuplicateIncludeGuard/header6.h | 16 +- .../Cleanup-DuplicateIncludeGuard/header7.h | 16 +- .../Cleanup-DuplicateIncludeGuard/main.c | 26 +- .../subfolder/header4.h | 16 +- .../subfolder/header5.h | 16 +- .../Linux_signed_chars/common.h | 40 +- .../Linux_unsigned_chars/common.h | 40 +- .../Microsoft/common.h | 40 +- .../Microsoft_no_wchar/common.h | 40 +- .../StackAddressEscapes/manager.cpp | 108 +- .../jsf/4.09 Style/AV Rule 53 54/test.CPP | 4 +- .../jsf/4.09 Style/AV Rule 53 54/test.c | 8 +- .../jsf/4.10 Classes/AV Rule 73/original.cpp | 320 +-- .../4.10 Classes/AV Rule 78/AV Rule 78.cpp | 216 +- .../successor-tests/assignexpr/assignexpr.cpp | 22 +- .../block/dummyblock/dummyblock.c | 8 +- .../block/emptyblock/emptyblock.c | 10 +- .../args/membercallexpr/membercallexpr.cpp | 22 +- .../staticmembercallexpr.cpp | 22 +- .../noargs/membercallexpr/membercallexpr.cpp | 20 +- .../nonmembercallexpr/nonmembercallexpr.c | 12 +- .../nonmemberfp2callexpr.c | 12 +- .../nonmemberfpcallexpr/nonmemberfpcallexpr.c | 10 +- .../callexpr/noargs/pmcallexpr/pmcallexpr.cpp | 20 +- .../staticmembercallexpr.cpp | 20 +- .../defdestructordeleteexpr.cpp | 12 +- .../deleteexpr/deleteexpr/deleteexpr.cpp | 18 +- cpp/ql/test/successor-tests/dostmt/dostmt.c | 76 +- cpp/ql/test/successor-tests/enum/enum.c | 14 +- .../ellipsisexceptionhandler.cpp | 34 +- .../exceptionhandler/exceptionhandler.cpp | 44 +- .../test/successor-tests/exprstmt/exprstmt.c | 8 +- .../constmemberaccess/constmemberaccess.cpp | 18 +- .../fieldaccess/fieldaccess.cpp | 18 +- .../forstmt/forstmt/forstmt.cpp | 40 +- .../forstmt/shortforstmt/shortforstmt.cpp | 76 +- .../forstmt/tinyforstmt/tinyforstmt.c | 10 +- .../test/successor-tests/gotostmt/gotostmt.c | 10 +- .../ifstmt/ifelsestmt/ifelsestmt.c | 90 +- .../successor-tests/ifstmt/ifstmt/ifstmt.c | 64 +- .../aggregateinitializer.c | 8 +- .../constructorinitializer.cpp | 18 +- .../initializer/initializer/initializer.c | 6 +- .../parameterinitializer.cpp | 62 +- .../test/successor-tests/landexpr/landexpr.c | 10 +- cpp/ql/test/successor-tests/lorexpr/lorexpr.c | 10 +- .../ltrbinopexpr/ltrbinopexpr.c | 86 +- .../defconstructornewexpr.cpp | 10 +- .../newexpr/newexpr/newexpr.cpp | 18 +- .../questionexpr/questionexpr.c | 6 +- .../successor-tests/returnstmt/returnstmt.c | 20 +- .../staticlocals/staticlocals.cpp | 26 +- .../subscriptexpr/revsubscriptexpr.c | 10 +- .../subscriptexpr/subscriptexpr.c | 10 +- .../nodefaultswitchstmt/nodefaultswitchstmt.c | 14 +- .../switchstmt/switchstmt/switchstmt.c | 16 +- .../successor-tests/unaryopexpr/unaryopexpr.c | 28 +- .../successor-tests/whilestmt/whilestmt.c | 90 +- 200 files changed, 5422 insertions(+), 5417 deletions(-) create mode 100644 cpp/ql/test/query-tests/AlertSuppression/.gitattributes diff --git a/.editorconfig b/.editorconfig index decc2081a24a..413e18ca6589 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp,html,js,mjs,ts,json,yml}] +[*.{ql,qll,qlref,dbscheme,qhelp,html,js,mjs,ts,json,yml,c,cpp,h,hpp}] end_of_line = lf diff --git a/.gitattributes b/.gitattributes index ec825a38b579..d945c85a0158 100644 --- a/.gitattributes +++ b/.gitattributes @@ -21,3 +21,7 @@ *.ts eol=lf *.json eol=lf *.yml eol=lf +*.c eol=lf +*.cpp eol=lf +*.h eol=lf +*.hpp eol=lf diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp index fea666c554d2..4a0564892716 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.cpp @@ -1,17 +1,17 @@ -// an include declaration just adds one source dependency, it does not automatically -// add a dependency from this file to all the declarations in stdio.h -#include -#include // contains non-static global myfile_err - -extern int myfile_err; // this external declaration adds a dependency on myfile.h - -class C { -public: - C() { - // one dependency for printf: - printf("Hello world!"); - // one dependency for FILE type, and one for NULL macro: - FILE fp = NULL; - } -}; - +// an include declaration just adds one source dependency, it does not automatically +// add a dependency from this file to all the declarations in stdio.h +#include +#include // contains non-static global myfile_err + +extern int myfile_err; // this external declaration adds a dependency on myfile.h + +class C { +public: + C() { + // one dependency for printf: + printf("Hello world!"); + // one dependency for FILE type, and one for NULL macro: + FILE fp = NULL; + } +}; + diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp index 0b576cc29b0d..987255a46d64 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.cpp @@ -1,20 +1,20 @@ -//This struct contains 30 fields. -struct MyParticle { - bool isActive; - int priority; - - float x, y, z; - float dx, dy, dz; - float ddx, ddy, ddz; - bool isCollider; - - int age, maxAge; - float size1, size2; - - bool hasColor; - unsigned char r1, g1, b1, a1; - unsigned char r2, g2, b2, a2; - - class texture *tex; - float u1, v1, u2, v2; -}; +//This struct contains 30 fields. +struct MyParticle { + bool isActive; + int priority; + + float x, y, z; + float dx, dy, dz; + float ddx, ddy, ddz; + bool isCollider; + + int age, maxAge; + float size1, size2; + + bool hasColor; + unsigned char r1, g1, b1, a1; + unsigned char r2, g2, b2, a2; + + class texture *tex; + float u1, v1, u2, v2; +}; diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp index 624226bff6ad..80d2c503feb4 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/FunctionsWithManyParameters.cpp @@ -1,8 +1,8 @@ -// this example has 15 parameters. -void fillRect(int x, int y, int w, int h, - int r1, int g1, int b1, int a1, - int r2, int g2, int b2, int a2, - gradient_type grad, unsigned int flags, bool border) -{ - // ... -} +// this example has 15 parameters. +void fillRect(int x, int y, int w, int h, + int r1, int g1, int b1, int a1, + int r2, int g2, int b2, int a2, + gradient_type grad, unsigned int flags, bool border) +{ + // ... +} diff --git a/cpp/ql/src/Best Practices/ComplexCondition.cpp b/cpp/ql/src/Best Practices/ComplexCondition.cpp index 8a8638c5bbe1..fba514919902 100644 --- a/cpp/ql/src/Best Practices/ComplexCondition.cpp +++ b/cpp/ql/src/Best Practices/ComplexCondition.cpp @@ -1,13 +1,13 @@ -//This condition is too complex and can be improved by using local variables -bool accept_message = - (message_type == CONNECT && _state != CONNECTED) || - (message_type == DISCONNECT && _state == CONNECTED) || - (message_type == DATA && _state == CONNECTED); - -//This condition is acceptable, as all the logical operators are of the same type (&&) -bool valid_connect = - message_type == CONNECT && - _state != CONNECTED && - time_since_prev_connect > MAX_CONNECT_INTERVAL && - message_length <= MAX_PACKET_SIZE && +//This condition is too complex and can be improved by using local variables +bool accept_message = + (message_type == CONNECT && _state != CONNECTED) || + (message_type == DISCONNECT && _state == CONNECTED) || + (message_type == DATA && _state == CONNECTED); + +//This condition is acceptable, as all the logical operators are of the same type (&&) +bool valid_connect = + message_type == CONNECT && + _state != CONNECTED && + time_since_prev_connect > MAX_CONNECT_INTERVAL && + message_length <= MAX_PACKET_SIZE && checksum(message) == get_checksum_field(message); \ No newline at end of file diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp index 687129d88152..374a3290ec71 100644 --- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.cpp @@ -1,6 +1,6 @@ -void f(int i) { - for (int i = 0; i < 10; ++i) { //the loop variable hides the parameter to f() - ... - } -} - +void f(int i) { + for (int i = 0; i < 10; ++i) { //the loop variable hides the parameter to f() + ... + } +} + diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp index bc96def17dfd..7cfe4617035d 100644 --- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp +++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.cpp @@ -1,12 +1,12 @@ -void f() { - int i = 10; - - for (int i = 0; i < 10; i++) { //the loop counter hides the variable - ... - } - - { - int i = 12; //this variable hides the variable in the outer block - ... - } -} +void f() { + int i = 10; + + for (int i = 0; i < 10; i++) { //the loop counter hides the variable + ... + } + + { + int i = 12; //this variable hides the variable in the outer block + ... + } +} diff --git a/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp index 61590c860111..89c473561a8b 100644 --- a/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp +++ b/cpp/ql/src/Best Practices/Hiding/LocalVariableHidesGlobalVariable.cpp @@ -1,12 +1,12 @@ -int i = 10; - -void f() { - for (int i = 0; i < 10; i++) { //the loop counter hides the global variable i - ... - } - - { - int i = 12; //this variable hides the global variable i - ... - } -} +int i = 10; + +void f() { + for (int i = 0; i < 10; i++) { //the loop counter hides the global variable i + ... + } + + { + int i = 12; //this variable hides the global variable i + ... + } +} diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp index 547810752ec8..a0a4c2d64ac7 100644 --- a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp +++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp @@ -1,9 +1,9 @@ -void f(int i) { - if (i == 10); //empty then block - ... //won't be part of the if statement - - if (i == 12) { - ... - } else { //empty else block, most likely a mistake - } -} +void f(int i) { + if (i == 10); //empty then block + ... //won't be part of the if statement + + if (i == 12) { + ... + } else { //empty else block, most likely a mistake + } +} diff --git a/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp b/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp index 09d1f414e5de..538516964a76 100644 --- a/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp +++ b/cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp @@ -1,43 +1,43 @@ -static int idctr = 0; -//Basic connection with id -class Connection { -public: - int connId; - virtual void print_info() { - cout << "id: " << connId << "\n"; - } - Connection() { - connId = idctr++; - } -}; - -//Adds counters, and an overriding print_info -class MeteredConnection : public Connection { -public: - int txCtr; - int rxCtr; - MeteredConnection() { - txCtr = 0; - rxCtr = 0; - } - virtual void print_info() { - cout << "id: " << connId << "\n" << "tx/rx: " << txCtr << "/" << rxCtr << "\n"; - } -}; - -int main(int argc, char* argv[]) { - Connection conn; - MeteredConnection m_conn; - - Connection curr_conn = conn; - curr_conn.print_info(); - curr_conn = m_conn; //Wrong: Derived MetricConnection assigned to Connection - //variable, will slice off the counters and the overriding print_info - curr_conn.print_info(); //Will not print the counters. - - Connection* curr_pconn = &conn; - curr_pconn->print_info(); - curr_pconn = &m_conn; //Correct: Pointer assigned to address of the MetricConnection. - //Counters and virtual functions remain intact. - curr_pconn->print_info(); //Will call the correct method MeteredConnection::print_info -} +static int idctr = 0; +//Basic connection with id +class Connection { +public: + int connId; + virtual void print_info() { + cout << "id: " << connId << "\n"; + } + Connection() { + connId = idctr++; + } +}; + +//Adds counters, and an overriding print_info +class MeteredConnection : public Connection { +public: + int txCtr; + int rxCtr; + MeteredConnection() { + txCtr = 0; + rxCtr = 0; + } + virtual void print_info() { + cout << "id: " << connId << "\n" << "tx/rx: " << txCtr << "/" << rxCtr << "\n"; + } +}; + +int main(int argc, char* argv[]) { + Connection conn; + MeteredConnection m_conn; + + Connection curr_conn = conn; + curr_conn.print_info(); + curr_conn = m_conn; //Wrong: Derived MetricConnection assigned to Connection + //variable, will slice off the counters and the overriding print_info + curr_conn.print_info(); //Will not print the counters. + + Connection* curr_pconn = &conn; + curr_pconn->print_info(); + curr_pconn = &m_conn; //Correct: Pointer assigned to address of the MetricConnection. + //Counters and virtual functions remain intact. + curr_pconn->print_info(); //Will call the correct method MeteredConnection::print_info +} diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp index f637c8d68e51..98f830132eb5 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.cpp @@ -1,16 +1,16 @@ -void sanitize(Fields[] record) { - //The number of fields here can be put in a const - for (fieldCtr = 0; field < 7; field++) { - sanitize(fields[fieldCtr]); - } -} - -#define NUM_FIELDS 7 - -void process(Fields[] record) { - //This avoids using a magic constant by using the macro instead - for (fieldCtr = 0; field < NUM_FIELDS; field++) { - process(fields[fieldCtr]); - } -} - +void sanitize(Fields[] record) { + //The number of fields here can be put in a const + for (fieldCtr = 0; field < 7; field++) { + sanitize(fields[fieldCtr]); + } +} + +#define NUM_FIELDS 7 + +void process(Fields[] record) { + //This avoids using a magic constant by using the macro instead + for (fieldCtr = 0; field < NUM_FIELDS; field++) { + process(fields[fieldCtr]); + } +} + diff --git a/cpp/ql/src/Best Practices/RuleOfTwo.cpp b/cpp/ql/src/Best Practices/RuleOfTwo.cpp index 4e602c10543d..d633f9db9801 100644 --- a/cpp/ql/src/Best Practices/RuleOfTwo.cpp +++ b/cpp/ql/src/Best Practices/RuleOfTwo.cpp @@ -1,26 +1,26 @@ -class C { -private: - Other* other = NULL; -public: - C(const C& copyFrom) { - Other* newOther = new Other(); - *newOther = copyFrom.other; - this->other = newOther; - } - - //No operator=, by default will just copy the pointer other, will not create a new object -}; - -class D { - Other* other = NULL; -public: - D& operator=(D& rhs) { - Other* newOther = new Other(); - *newOther = rhs.other; - this->other = newOther; - return *this; - } - - //No copy constructor, will just copy the pointer other and not create a new object -}; - +class C { +private: + Other* other = NULL; +public: + C(const C& copyFrom) { + Other* newOther = new Other(); + *newOther = copyFrom.other; + this->other = newOther; + } + + //No operator=, by default will just copy the pointer other, will not create a new object +}; + +class D { + Other* other = NULL; +public: + D& operator=(D& rhs) { + Other* newOther = new Other(); + *newOther = rhs.other; + this->other = newOther; + return *this; + } + + //No copy constructor, will just copy the pointer other and not create a new object +}; + diff --git a/cpp/ql/src/Best Practices/SwitchLongCase.cpp b/cpp/ql/src/Best Practices/SwitchLongCase.cpp index 422c3fdbf562..7b8a0e668085 100644 --- a/cpp/ql/src/Best Practices/SwitchLongCase.cpp +++ b/cpp/ql/src/Best Practices/SwitchLongCase.cpp @@ -1,32 +1,32 @@ -//This switch statement has long case statements, and can become difficult to -//read as the processing for each message type becomes more complex -switch (message_type) { - case CONNECT: - _state = CONNECTING; - int message_id = message_get_id(message); - int source = connect_get_source(message); - //More code here... - send(connect_response); - break; - case DISCONNECT: - _state = DISCONNECTING; - int message_id = message_get_id(message); - int source = disconnect_get_source(message); - //More code here... - send(disconnect_response); - break; - default: - log("Invalid message, id : %d", message_get_id(message)); -} - -//This is better, as each case is split out to a separate function -switch (packet_type) { - case STREAM: - process_stream_packet(packet); - break; - case DATAGRAM: - process_datagram_packet(packet); - break; - default: - log("Invalid packet type: %d", packet_type); +//This switch statement has long case statements, and can become difficult to +//read as the processing for each message type becomes more complex +switch (message_type) { + case CONNECT: + _state = CONNECTING; + int message_id = message_get_id(message); + int source = connect_get_source(message); + //More code here... + send(connect_response); + break; + case DISCONNECT: + _state = DISCONNECTING; + int message_id = message_get_id(message); + int source = disconnect_get_source(message); + //More code here... + send(disconnect_response); + break; + default: + log("Invalid message, id : %d", message_get_id(message)); +} + +//This is better, as each case is split out to a separate function +switch (packet_type) { + case STREAM: + process_stream_packet(packet); + break; + case DATAGRAM: + process_datagram_packet(packet); + break; + default: + log("Invalid packet type: %d", packet_type); } \ No newline at end of file diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp index 0f20fd9d5291..277e753b06b3 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.cpp @@ -1,5 +1,5 @@ -{ - int x = 0; //x is unused - int y = 0; - cout << y; -} +{ + int x = 0; //x is unused + int y = 0; + cout << y; +} diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp index 9e2685ea7c47..a1b259e2c47b 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticFunctions.cpp @@ -1,14 +1,14 @@ -//start of file -static void f() { //static function f() is unused in the file - //... -} -static void g() { - //... -} -void public_func() { //non-static function public_func is not called in file, - //but could be visible in other files - //... - g(); //call to g() - //... -} -//end of file +//start of file +static void f() { //static function f() is unused in the file + //... +} +static void g() { + //... +} +void public_func() { //non-static function public_func is not called in file, + //but could be visible in other files + //... + g(); //call to g() + //... +} +//end of file diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp index 4627aab67b3a..c5d92cc6eefd 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.cpp @@ -1,5 +1,5 @@ -void f() { - static int i = 0; //i is unused - ... - return; -} +void f() { + static int i = 0; //i is unused + ... + return; +} diff --git a/cpp/ql/src/Critical/DeadCodeCondition.cpp b/cpp/ql/src/Critical/DeadCodeCondition.cpp index 743dc39c1b31..1a0eec18bcaa 100644 --- a/cpp/ql/src/Critical/DeadCodeCondition.cpp +++ b/cpp/ql/src/Critical/DeadCodeCondition.cpp @@ -1,19 +1,19 @@ -while(result) { - if ( ... ) - ... - else if (result //wrong: this test is redundant - && result->flags != 0) - ... - result = next(queue); -} - - -fp = fopen(log, "r"); -if (fp) { - /* - * large block of code - */ - if (!fp) { //wrong: always false - ... /* dead code */ - } -} +while(result) { + if ( ... ) + ... + else if (result //wrong: this test is redundant + && result->flags != 0) + ... + result = next(queue); +} + + +fp = fopen(log, "r"); +if (fp) { + /* + * large block of code + */ + if (!fp) { //wrong: always false + ... /* dead code */ + } +} diff --git a/cpp/ql/src/Critical/DeadCodeFunction.cpp b/cpp/ql/src/Critical/DeadCodeFunction.cpp index e16dc9c3bf54..1cdcc652d97e 100644 --- a/cpp/ql/src/Critical/DeadCodeFunction.cpp +++ b/cpp/ql/src/Critical/DeadCodeFunction.cpp @@ -1,12 +1,12 @@ -class C { -public: - void g() { - ... - //f() was previously used but is now commented, orphaning f() - //f(); - ... - } -private: - void f() { //is now unused, and can be removed - } -}; +class C { +public: + void g() { + ... + //f() was previously used but is now commented, orphaning f() + //f(); + ... + } +private: + void f() { //is now unused, and can be removed + } +}; diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp index 69b83a9a4904..a29e0d634d72 100644 --- a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.cpp @@ -1,9 +1,9 @@ -int f() { - try { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - do_stuff(sockfd); - return sockfd; //if there are no exceptions, the socket is returned - } catch (int do_stuff_exception) { - return -1; //return error value, but sockfd may still be open - } -} +int f() { + try { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + do_stuff(sockfd); + return sockfd; //if there are no exceptions, the socket is returned + } catch (int do_stuff_exception) { + return -1; //return error value, but sockfd may still be open + } +} diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.cpp b/cpp/ql/src/Critical/DescriptorNeverClosed.cpp index d791a7024bff..137e962ac646 100644 --- a/cpp/ql/src/Critical/DescriptorNeverClosed.cpp +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.cpp @@ -1,6 +1,6 @@ -int main(int argc, char* argv[]) { - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - int status = 0; - ... //code that does not close sockfd - return status; //sockfd is never closed -} +int main(int argc, char* argv[]) { + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + int status = 0; + ... //code that does not close sockfd + return status; //sockfd is never closed +} diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp b/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp index 30fb4ca1cee0..dc3a35c8aae6 100644 --- a/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp +++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.cpp @@ -1,11 +1,11 @@ -int g_callCtr; - -void initGlobals() { - g_callCtr = 0; -} - -int main(int argc, char* argv[]) { - ... - cout << g_callCtr; //callCtr used before it is initialized - initGlobals(); -} +int g_callCtr; + +void initGlobals() { + g_callCtr = 0; +} + +int main(int argc, char* argv[]) { + ... + cout << g_callCtr; //callCtr used before it is initialized + initGlobals(); +} diff --git a/cpp/ql/src/Critical/InitialisationNotRun.cpp b/cpp/ql/src/Critical/InitialisationNotRun.cpp index 2c937d4b9b90..ab67945e6365 100644 --- a/cpp/ql/src/Critical/InitialisationNotRun.cpp +++ b/cpp/ql/src/Critical/InitialisationNotRun.cpp @@ -1,12 +1,12 @@ -GlobalStorage *g_storage; - -void init() { //initializes g_storage, but is never run from main - g_storage = new GlobalStorage(); - ... -} - -int main(int argc, char *argv[]) { - ... //init not called - strcpy(g_storage->name, argv[1]); // g_storage is used before init() is called - ... -} +GlobalStorage *g_storage; + +void init() { //initializes g_storage, but is never run from main + g_storage = new GlobalStorage(); + ... +} + +int main(int argc, char *argv[]) { + ... //init not called + strcpy(g_storage->name, argv[1]); // g_storage is used before init() is called + ... +} diff --git a/cpp/ql/src/Critical/LargeParameter.cpp b/cpp/ql/src/Critical/LargeParameter.cpp index 5e829636132e..64a05b85df98 100644 --- a/cpp/ql/src/Critical/LargeParameter.cpp +++ b/cpp/ql/src/Critical/LargeParameter.cpp @@ -1,13 +1,13 @@ -typedef struct Names { - char first[100]; - char last[100]; -} Names; - -int doFoo(Names n) { //wrong: n is passed by value (meaning the entire structure - //is copied onto the stack, instead of just a pointer) - ... -} - -int doBar(Names &n) { //better, only a reference is passed - ... -} +typedef struct Names { + char first[100]; + char last[100]; +} Names; + +int doFoo(Names n) { //wrong: n is passed by value (meaning the entire structure + //is copied onto the stack, instead of just a pointer) + ... +} + +int doBar(Names &n) { //better, only a reference is passed + ... +} diff --git a/cpp/ql/src/Critical/MissingNullTest.cpp b/cpp/ql/src/Critical/MissingNullTest.cpp index 074f9d285e93..05f2ba2278a6 100644 --- a/cpp/ql/src/Critical/MissingNullTest.cpp +++ b/cpp/ql/src/Critical/MissingNullTest.cpp @@ -1,11 +1,11 @@ -typedef struct { - char name[100]; - int status; -} person; - -void f() { - person* buf = NULL; - buf = malloc(sizeof(person)); - - (*buf).status = 0; //access to buf before it was checked for NULL -} +typedef struct { + char name[100]; + int status; +} person; + +void f() { + person* buf = NULL; + buf = malloc(sizeof(person)); + + (*buf).status = 0; //access to buf before it was checked for NULL +} diff --git a/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp b/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp index 2ee7ed26732e..94265b6262b0 100644 --- a/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp +++ b/cpp/ql/src/Critical/NewArrayDeleteMismatch.cpp @@ -1,5 +1,5 @@ -Record* record = new Record[SIZE]; - -... - -delete record; //record was created using 'new[]', but was freed using 'delete' +Record* record = new Record[SIZE]; + +... + +delete record; //record was created using 'new[]', but was freed using 'delete' diff --git a/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp b/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp index 31aaa24a8320..4ee57b7e20e5 100644 --- a/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp +++ b/cpp/ql/src/Critical/NewDeleteArrayMismatch.cpp @@ -1,5 +1,5 @@ -Record *ptr = new Record(...); - -... - -delete [] ptr; // ptr was created using 'new', but was freed using 'delete[]' +Record *ptr = new Record(...); + +... + +delete [] ptr; // ptr was created using 'new', but was freed using 'delete[]' diff --git a/cpp/ql/src/Critical/NewFreeMismatch.cpp b/cpp/ql/src/Critical/NewFreeMismatch.cpp index 4a359c0ffa53..14ae95415de2 100644 --- a/cpp/ql/src/Critical/NewFreeMismatch.cpp +++ b/cpp/ql/src/Critical/NewFreeMismatch.cpp @@ -1,5 +1,5 @@ -Record *ptr = new Record(...); - -... - -free(ptr); // BAD: ptr was created using 'new', but is being freed using 'free' +Record *ptr = new Record(...); + +... + +free(ptr); // BAD: ptr was created using 'new', but is being freed using 'free' diff --git a/cpp/ql/src/Critical/NotInitialised.cpp b/cpp/ql/src/Critical/NotInitialised.cpp index 7d0e6b8f9bec..af9b733e9622 100644 --- a/cpp/ql/src/Critical/NotInitialised.cpp +++ b/cpp/ql/src/Critical/NotInitialised.cpp @@ -1,6 +1,6 @@ -{ - int i; - - ... - int g = COEFF * i; //i is used before it is initialized -} +{ + int i; + + ... + int g = COEFF * i; //i is used before it is initialized +} diff --git a/cpp/ql/src/Critical/OverflowDestination.cpp b/cpp/ql/src/Critical/OverflowDestination.cpp index d6413231bbf0..1a758430bf4e 100644 --- a/cpp/ql/src/Critical/OverflowDestination.cpp +++ b/cpp/ql/src/Critical/OverflowDestination.cpp @@ -1,13 +1,13 @@ - -int main(int argc, char* argv[]) { - char param[SIZE]; - - char arg1[10]; - char arg2[20]; - - //wrong: only uses the size of the source (argv[1]) when using strncpy - strncpy(param, argv[1], strlen(arg1)); - - //correct: uses the size of the destination array as well - strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1))); -} + +int main(int argc, char* argv[]) { + char param[SIZE]; + + char arg1[10]; + char arg2[20]; + + //wrong: only uses the size of the source (argv[1]) when using strncpy + strncpy(param, argv[1], strlen(arg1)); + + //correct: uses the size of the destination array as well + strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1))); +} diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.cpp b/cpp/ql/src/Critical/ReturnValueIgnored.cpp index d68d5021bbaa..5e11e765eb03 100644 --- a/cpp/ql/src/Critical/ReturnValueIgnored.cpp +++ b/cpp/ql/src/Critical/ReturnValueIgnored.cpp @@ -1,23 +1,23 @@ -int doFoo() { - ... - return status; -} - -void f() { - if (doFoo() == OK) { - ... - } -} - -void g() { - int status = doFoo(); - if (status == OK) { - ... - } -} - -void err() { - doFoo(); //doFoo is called but its return value is not checked, and - //the value is checked in other locations - ... -} +int doFoo() { + ... + return status; +} + +void f() { + if (doFoo() == OK) { + ... + } +} + +void g() { + int status = doFoo(); + if (status == OK) { + ... + } +} + +void err() { + doFoo(); //doFoo is called but its return value is not checked, and + //the value is checked in other locations + ... +} diff --git a/cpp/ql/src/Critical/SizeCheck.cpp b/cpp/ql/src/Critical/SizeCheck.cpp index 6400e9d4fb54..5f3ec91ad1e7 100644 --- a/cpp/ql/src/Critical/SizeCheck.cpp +++ b/cpp/ql/src/Critical/SizeCheck.cpp @@ -1,10 +1,10 @@ -#define RECORD_SIZE 30 //incorrect or outdated size for record -typedef struct { - char name[30]; - int status; -} Record; - -void f() { - Record* p = malloc(RECORD_SIZE); //not of sufficient size to hold a Record - ... -} +#define RECORD_SIZE 30 //incorrect or outdated size for record +typedef struct { + char name[30]; + int status; +} Record; + +void f() { + Record* p = malloc(RECORD_SIZE); //not of sufficient size to hold a Record + ... +} diff --git a/cpp/ql/src/Critical/Unused.cpp b/cpp/ql/src/Critical/Unused.cpp index 01c60357397e..bda3329d7c0c 100644 --- a/cpp/ql/src/Critical/Unused.cpp +++ b/cpp/ql/src/Critical/Unused.cpp @@ -1,4 +1,4 @@ -{ - int foo = 1; - ... //foo is unused -} +{ + int foo = 1; + ... //foo is unused +} diff --git a/cpp/ql/src/Critical/UseAfterFree.cpp b/cpp/ql/src/Critical/UseAfterFree.cpp index 448696b4bb7b..da921a6a31aa 100644 --- a/cpp/ql/src/Critical/UseAfterFree.cpp +++ b/cpp/ql/src/Critical/UseAfterFree.cpp @@ -1,9 +1,9 @@ -int f() { - char* buf = new char[SIZE]; - .... - if (error) { - free(buf); //error handling has freed the buffer - } - ... - log_contents(buf); //but it is still used here for logging -} +int f() { + char* buf = new char[SIZE]; + .... + if (error) { + free(buf); //error handling has freed the buffer + } + ... + log_contents(buf); //but it is still used here for logging +} diff --git a/cpp/ql/src/Documentation/TodoComments.cpp b/cpp/ql/src/Documentation/TodoComments.cpp index 2404ca2b2e9c..15cfb35b4e9c 100644 --- a/cpp/ql/src/Documentation/TodoComments.cpp +++ b/cpp/ql/src/Documentation/TodoComments.cpp @@ -1,4 +1,4 @@ -int isOdd(int n) { - //TODO: Works only for positive n. Need to check if negative n is valid input - return (n % 2) == 1; +int isOdd(int n) { + //TODO: Works only for positive n. Need to check if negative n is valid input + return (n % 2) == 1; } \ No newline at end of file diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp index 2b4e70c98ab0..30904384a3a8 100644 --- a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.cpp @@ -1,8 +1,8 @@ -// header_file.h - -#ifndef HEADER_FILE_H -#define HEADER_FILE_H - - // ... - +// header_file.h + +#ifndef HEADER_FILE_H +#define HEADER_FILE_H + + // ... + #endif // HEADER_FILE_H \ No newline at end of file diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp index 3a5d7afd9c24..5057800dcf64 100644 --- a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp +++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard2.cpp @@ -1,8 +1,8 @@ -// another_header_file.h - -#ifndef HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H -#define HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H - - // ... - +// another_header_file.h + +#ifndef HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H +#define HEADER_FILE_H // should be ANOTHER_HEADER_FILE_H + + // ... + #endif // HEADER_FILE_H \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp index 07f1a0529e90..34b7239ba410 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.cpp @@ -1,6 +1,6 @@ -void h() { - int a, b, c; - - a < b != c; //parenthesize to explicitly define order of operators - (a < b) < c; //correct: parenthesized to specify order -} +void h() { + int a, b, c; + + a < b != c; //parenthesize to explicitly define order of operators + (a < b) < c; //correct: parenthesized to specify order +} diff --git a/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp index eff2e167c1d9..20c8b031d783 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp +++ b/cpp/ql/src/Likely Bugs/Conversion/ArrayArgSizeMismatch.cpp @@ -1,13 +1,13 @@ - -//Function foo's array parameter has a specified size -void foo(int a[10]) { - int i = 0; - for (i = 0; i <10; i++) { - a[i] = i * 2; - } -} - -... - -int my_arr[5]; + +//Function foo's array parameter has a specified size +void foo(int a[10]) { + int i = 0; + for (i = 0; i <10; i++) { + a[i] = i * 2; + } +} + +... + +int my_arr[5]; foo(my_arr); //my_arr is smaller than foo's array parameter, and will cause access to memory outside its bounds \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp index f2f049de4a75..e19d001dd9f9 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp +++ b/cpp/ql/src/Likely Bugs/Conversion/ConversionChangesSign.cpp @@ -1,4 +1,4 @@ -//sz is a signed integer, but malloc expects one that is unsigned. -//Negative values will be interpreted as a large number, which may -//lead to unexpected behavior -char *buf = malloc(sz); +//sz is a signed integer, but malloc expects one that is unsigned. +//Negative values will be interpreted as a large number, which may +//lead to unexpected behavior +char *buf = malloc(sz); diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp index 9f9bc1b08a8a..a96b399541a5 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyPointerCast.cpp @@ -1,5 +1,5 @@ -void f(char *p) { - int my_ptr = p; //Wrong: pointer assigned to int, would be incorrect if sizeof(char*) - //is larger than sizeof(int) - //... -} +void f(char *p) { + int my_ptr = p; //Wrong: pointer assigned to int, would be incorrect if sizeof(char*) + //is larger than sizeof(int) + //... +} diff --git a/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp index 69f25116ca14..302822f74371 100644 --- a/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp +++ b/cpp/ql/src/Likely Bugs/InconsistentCheckReturnNull.cpp @@ -1,30 +1,30 @@ -struct property { - char *name; - int value; -}; - -struct property * get_property(char *key); -struct property * get_property_default(char *key, int default_value); - -void check_properties() { - // this call will get flagged since most - // calls to get_property handle NULL - struct property *p1 = get_property("time"); - if(p1->value > 600) { - ... - } - - // this call will not get flagged since - // the result of the call is checked for NULL - struct property *p2 = get_property("time"); - if(p2 != NULL && p2->value > 600) { - ... - } - - // this call will not get flagged since calls - // to get_property_default rarely handle NULL - struct property *p3 = get_property_default("time", 50); - if(p3->value > 60) { - ... - } -} +struct property { + char *name; + int value; +}; + +struct property * get_property(char *key); +struct property * get_property_default(char *key, int default_value); + +void check_properties() { + // this call will get flagged since most + // calls to get_property handle NULL + struct property *p1 = get_property("time"); + if(p1->value > 600) { + ... + } + + // this call will not get flagged since + // the result of the call is checked for NULL + struct property *p2 = get_property("time"); + if(p2 != NULL && p2->value > 600) { + ... + } + + // this call will not get flagged since calls + // to get_property_default rarely handle NULL + struct property *p3 = get_property_default("time", 50); + if(p3->value > 60) { + ... + } +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp index 6774921fb0ce..155bba73e219 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp +++ b/cpp/ql/src/Likely Bugs/Likely Typos/BoolValueInBitOp.cpp @@ -1,13 +1,13 @@ -if (!flags & SOME_BIT) { //wrong: '!' has higher precedence than '&', so this - // is bracketed as '(!flags) & SOME_BIT', and does not - // check whether a particular bit is set. - // ... -} - -if ((p != NULL) & p->f()) { //wrong: The use of '&' rather than '&&' will still - // de-reference the pointer even if it is NULL. - // ... -} - -int bits = (s > 8) & 0xff; //wrong: Invalid attempt to get the 8 most significant - // bits of a short. +if (!flags & SOME_BIT) { //wrong: '!' has higher precedence than '&', so this + // is bracketed as '(!flags) & SOME_BIT', and does not + // check whether a particular bit is set. + // ... +} + +if ((p != NULL) & p->f()) { //wrong: The use of '&' rather than '&&' will still + // de-reference the pointer even if it is NULL. + // ... +} + +int bits = (s > 8) & 0xff; //wrong: Invalid attempt to get the 8 most significant + // bits of a short. diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp index f2de7c43c92f..d54ca4adec28 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp +++ b/cpp/ql/src/Likely Bugs/Likely Typos/LogicalExprCouldBeSimplified.cpp @@ -1,11 +1,11 @@ -if (0 || condition) { //wrong: can be converted to just 'condition' - //... -} - -if (0 && condition) { //wrong: always evaluates to false, if statement can be removed - // ... -} - -if ('A' == 65 && condition) { // wrong: can be converted to just 'condition' - // ... -} +if (0 || condition) { //wrong: can be converted to just 'condition' + //... +} + +if (0 && condition) { //wrong: always evaluates to false, if statement can be removed + // ... +} + +if ('A' == 65 && condition) { // wrong: can be converted to just 'condition' + // ... +} diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp index cec5e9337e8c..11eb9de130ef 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.cpp @@ -1,30 +1,30 @@ -typedef enum { - RED, - ORANGE, - YELLOW, - GREEN, - BLUE, - INDIGO, - VIOLET -} colors; - -int f(colors c) { - switch (c) { - case RED: - //... - case GREEN: - //... - case BLUE: - //... - //wrong: does not use all enum values, and has no default - } - - switch(c) { - case RED: - //... - case GREEN: - //... - default: - //correct: does not use all enum values, but has a default - } -} +typedef enum { + RED, + ORANGE, + YELLOW, + GREEN, + BLUE, + INDIGO, + VIOLET +} colors; + +int f(colors c) { + switch (c) { + case RED: + //... + case GREEN: + //... + case BLUE: + //... + //wrong: does not use all enum values, and has no default + } + + switch(c) { + case RED: + //... + case GREEN: + //... + default: + //correct: does not use all enum values, but has a default + } +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp index efb2569797c9..cf83c251efc0 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp +++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.cpp @@ -1,12 +1,12 @@ -void f(char* s, float f) { - char buf[30]; - - //wrong: gets has no limit to the length of data it puts in the buffer - gets(buf); - - //wrong: sprintf does not limit the length of the string put into buf - sprintf(buf, "This is a string: %s", s); - - //wrong: %f can expand to a very long string in extreme cases, easily overrunning this buffer - sprintf(buf, "This is a float: %f", f); -} +void f(char* s, float f) { + char buf[30]; + + //wrong: gets has no limit to the length of data it puts in the buffer + gets(buf); + + //wrong: sprintf does not limit the length of the string put into buf + sprintf(buf, "This is a string: %s", s); + + //wrong: %f can expand to a very long string in extreme cases, easily overrunning this buffer + sprintf(buf, "This is a float: %f", f); +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp index 219d082414dd..c5cbcd2d7f1e 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp @@ -1,4 +1,4 @@ -strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest - -strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. - //Also fails if dest is a pointer and not an array. +strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest + +strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. + //Also fails if dest is a pointer and not an array. diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp index 7c38d27a73a9..ba76ea2dad7e 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.cpp @@ -1,4 +1,4 @@ -void f(char s[]) { - int size = sizeof(s); //wrong: s is now a char*, not an array. - //sizeof(s) will evaluate to sizeof(char *) -} +void f(char s[]) { + int size = sizeof(s); //wrong: s is now a char*, not an array. + //sizeof(s) will evaluate to sizeof(char *) +} diff --git a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp index 7dd968cec7d0..96aed351778b 100644 --- a/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp +++ b/cpp/ql/src/Likely Bugs/NestedLoopSameVar.cpp @@ -1,16 +1,16 @@ -int x1 = 0; -for (x1 = 0; x1 < 100; x1++) { - int x2 = 0; - for (x1 = 0; x1 < 300; x1++) { - // this is most likely a typo - // the outer loop will exit immediately - } -} - -for (x1 = 0; x1 < 100; x1++) { - if(x1 == 10 && condition) { - for (; x1 < 75; x1++) { - // this should be written as a while loop - } - } -} +int x1 = 0; +for (x1 = 0; x1 < 100; x1++) { + int x2 = 0; + for (x1 = 0; x1 < 300; x1++) { + // this is most likely a typo + // the outer loop will exit immediately + } +} + +for (x1 = 0; x1 < 100; x1++) { + if(x1 == 10 && condition) { + for (; x1 < 75; x1++) { + // this should be written as a while loop + } + } +} diff --git a/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp index 93ceb8dd1114..cc9fae1221fb 100644 --- a/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp +++ b/cpp/ql/src/Likely Bugs/OO/NonVirtualDestructor.cpp @@ -1,31 +1,31 @@ -class Base { -public: - Resource *p; - Base() { - p = createResource(); - } - //... - ~Base() { - //wrong: this destructor is non-virtual, but Base has a derived class - // with a non-virtual destructor - freeResource(p); - } -}; - -class Derived: public Base { -public: - Resource *dp; - Derived() { - dp = createResource2(); - } - ~Derived() { - freeResource2(dp); - } -}; - -int f() { - Base *b = new Derived(); //creates resources for both Base::p and Derived::dp - //... - delete b; //will only call Base::~Base(), leaking the resource dp. - // Change both destructors to virtual to ensure they are both called. -} +class Base { +public: + Resource *p; + Base() { + p = createResource(); + } + //... + ~Base() { + //wrong: this destructor is non-virtual, but Base has a derived class + // with a non-virtual destructor + freeResource(p); + } +}; + +class Derived: public Base { +public: + Resource *dp; + Derived() { + dp = createResource2(); + } + ~Derived() { + freeResource2(dp); + } +}; + +int f() { + Base *b = new Derived(); //creates resources for both Base::p and Derived::dp + //... + delete b; //will only call Base::~Base(), leaking the resource dp. + // Change both destructors to virtual to ensure they are both called. +} diff --git a/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp index 77f95e0b2573..d7dcbec116a3 100644 --- a/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp +++ b/cpp/ql/src/Likely Bugs/OO/ThrowInDestructor.cpp @@ -1,19 +1,19 @@ -class C { -public: - //... - ~C(){ - if (error) { - throw "Exception in destructor"; //wrong: exception thrown in destructor - } - } -}; - -void f() { - C* c = new C(); - try { - doOperation(c); - delete c; - } catch ( char * do_operation_exception) { - delete c; //would immediately terminate program if C::~C throws an exception - } -} +class C { +public: + //... + ~C(){ + if (error) { + throw "Exception in destructor"; //wrong: exception thrown in destructor + } + } +}; + +void f() { + C* c = new C(); + try { + doOperation(c); + delete c; + } catch ( char * do_operation_exception) { + delete c; //would immediately terminate program if C::~C throws an exception + } +} diff --git a/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp b/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp index 568c994ec640..c1d299c231f5 100644 --- a/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp +++ b/cpp/ql/src/Likely Bugs/ShortLoopVarName.cpp @@ -1,14 +1,14 @@ -int i = 0; -for (i = 0; i < NUM_RECORDS; i++) { - int j = 0; - //This loop should have a more descriptive iteration variable - for (j = 0; j < NUM_FIELDS; j++) { - process(record[i]->field[j]); - } - - int field_idx = 0; - //Better: the inner loop has a descriptive name - for (field_idx = 0; field_idx < NUM_FIELDS; field_idx++) { - save(record[i]->field[field_idx]); - } +int i = 0; +for (i = 0; i < NUM_RECORDS; i++) { + int j = 0; + //This loop should have a more descriptive iteration variable + for (j = 0; j < NUM_FIELDS; j++) { + process(record[i]->field[j]); + } + + int field_idx = 0; + //Better: the inner loop has a descriptive name + for (field_idx = 0; field_idx < NUM_FIELDS; field_idx++) { + save(record[i]->field[field_idx]); + } } \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c index 4f3b0dd2cf49..62acb4d15e5a 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.c @@ -1,28 +1,28 @@ -void main(int argc, char **argv) { - uint32_t big_num = INT32_MAX; - char buf[big_num]; - int16_t bytes_received = 0; - int max_get = INT16_MAX + 1; - - // BAD: 'bytes_received' is compared with a value of a wider type. - // 'bytes_received' overflows before reaching 'max_get', - // causing an infinite loop - while (bytes_received < max_get) - bytes_received += get_from_input(buf, bytes_received); - } - - uint32_t bytes_received = 0; - - // GOOD: 'bytes_received2' has a type at least as wide as 'max_get' - while (bytes_received < max_get) { - bytes_received += get_from_input(buf, bytes_received); - } - -} - - -int getFromInput(char *buf, short pos) { - // write to buf - // ... - return 1; -} +void main(int argc, char **argv) { + uint32_t big_num = INT32_MAX; + char buf[big_num]; + int16_t bytes_received = 0; + int max_get = INT16_MAX + 1; + + // BAD: 'bytes_received' is compared with a value of a wider type. + // 'bytes_received' overflows before reaching 'max_get', + // causing an infinite loop + while (bytes_received < max_get) + bytes_received += get_from_input(buf, bytes_received); + } + + uint32_t bytes_received = 0; + + // GOOD: 'bytes_received2' has a type at least as wide as 'max_get' + while (bytes_received < max_get) { + bytes_received += get_from_input(buf, bytes_received); + } + +} + + +int getFromInput(char *buf, short pos) { + // write to buf + // ... + return 1; +} diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp index 2c9a76e676ed..3ae1ca4db662 100644 --- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp +++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 95.cpp @@ -1,20 +1,20 @@ -enum Shape_color { red, green, blue }; -class Shape -{ - public: - virtual void draw (Shape_color color = green) const; - ... -} -class Circle : public Shape -{ - public: - virtual void draw (Shape_color color = red) const; - ... -} -void fun() -{ - Shape* sp; - - sp = new Circle; - sp->draw (); // Invokes Circle::draw(green) even though the default -} // parameter for Circle is red. +enum Shape_color { red, green, blue }; +class Shape +{ + public: + virtual void draw (Shape_color color = green) const; + ... +} +class Circle : public Shape +{ + public: + virtual void draw (Shape_color color = red) const; + ... +} +void fun() +{ + Shape* sp; + + sp = new Circle; + sp->draw (); // Invokes Circle::draw(green) even though the default +} // parameter for Circle is red. diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/common.h b/cpp/ql/test/header-variant-tests/multi-target-includes/common.h index 8c20e36220b3..a965769ffd64 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/common.h +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/common.h @@ -1,5 +1,5 @@ -// common.h - -#include "nameclash.h" - +// common.h + +#include "nameclash.h" + static int myArray[sizeof(MYTYPE)]; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h b/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h index f80f739121e9..ea10509143e5 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/defines_issue.h @@ -1,3 +1,3 @@ -// defines_issue.h - +// defines_issue.h + #include DEFINED_HEADER \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp b/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp index 49ec6536942e..9c4ba0e202cc 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/main2.cpp @@ -1,7 +1,7 @@ -// semmle-extractor-options: -I${testdir}/subdir2 -// main2.cpp -#include "common.h" - -#define DEFINED_HEADER "define2.h" - -#include "defines_issue.h" +// semmle-extractor-options: -I${testdir}/subdir2 +// main2.cpp +#include "common.h" + +#define DEFINED_HEADER "define2.h" + +#include "defines_issue.h" diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h index 9ab85dc2b12e..bcfe8abed540 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/nameclash.h @@ -1,3 +1,3 @@ -// nameclash.h - +// nameclash.h + #include_next "nameclash.h" \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h index d0361b16bdd9..f4a942327c22 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir1/nameclash.h @@ -1,3 +1,3 @@ -// subdir1/nameclash.h - +// subdir1/nameclash.h + typedef long long int MYTYPE; \ No newline at end of file diff --git a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h index 469a0c9b3039..0ffbb1fd89b2 100644 --- a/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h +++ b/cpp/ql/test/header-variant-tests/multi-target-includes/subdir2/nameclash.h @@ -1,3 +1,3 @@ -// subdir2/nameclash.h - +// subdir2/nameclash.h + typedef char MYTYPE; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/allocators/allocators.cpp b/cpp/ql/test/library-tests/allocators/allocators.cpp index a32c530f5835..5f0a535e7f3b 100644 --- a/cpp/ql/test/library-tests/allocators/allocators.cpp +++ b/cpp/ql/test/library-tests/allocators/allocators.cpp @@ -1,111 +1,111 @@ -// semmle-extractor-options: -std=c++17 -typedef unsigned long size_t; -namespace std { - enum class align_val_t : size_t {}; -} - -void* operator new(size_t, float); -void* operator new[](size_t, float); -void* operator new(size_t, std::align_val_t, float); -void* operator new[](size_t, std::align_val_t, float); -void operator delete(void*, float); -void operator delete[](void*, float); -void operator delete(void*, std::align_val_t, float); -void operator delete[](void*, std::align_val_t, float); - -struct String { - String(); - String(const String&); - String(String&&); - String(const char*); - ~String(); - - String& operator=(const String&); - String& operator=(String&&); - - const char* c_str() const; - -private: - const char* p; -}; - -struct SizedDealloc { - char a[32]; - void* operator new(size_t); - void* operator new[](size_t); - void operator delete(void*, size_t); - void operator delete[](void*, size_t); -}; - -struct alignas(128) Overaligned { - char a[256]; -}; - -struct PolymorphicBase { - virtual ~PolymorphicBase(); -}; - -void OperatorNew() { - new int; // No constructor - new(1.0f) int; // Placement new, no constructor - new int(); // Zero-init - new String(); // Constructor - new(1.0f) String("hello"); // Placement new, constructor with args - new Overaligned; // Aligned new - new(1.0f) Overaligned(); // Placement aligned new -} - -void OperatorDelete() { - delete static_cast(nullptr); // No destructor - delete static_cast(nullptr); // Non-virtual destructor, with size. - delete static_cast(nullptr); // No destructor, with size. - delete static_cast(nullptr); // No destructor, with size and alignment. - delete static_cast(nullptr); // Virtual destructor - delete static_cast(nullptr); // Pointer to const -} - -void OperatorNewArray(int n) { - new int[n]; // No constructor - new(1.0f) int[n]; // Placement new, no constructor - new String[n]; // Constructor - new Overaligned[n]; // Aligned new - new String[10]; // Constant size -} - -int* const GetPointer(); - -void OperatorDeleteArray() { - delete[] static_cast(nullptr); // No destructor - delete[] static_cast(nullptr); // Non-virtual destructor, with size. - delete[] static_cast(nullptr); // No destructor, with size. - delete[] static_cast(nullptr); // No destructor, with size and alignment. - delete[] static_cast(nullptr); // Virtual destructor - delete[] GetPointer(); -} - -struct FailedInit { - FailedInit(); - ~FailedInit(); - - void* operator new(size_t); // Non-placement - void* operator new[](size_t); // Non-placement - void operator delete(void*, size_t); // Sized deallocation - void operator delete[](void*, size_t); // Sized deallocation -}; - -struct alignas(128) FailedInitOveraligned { - FailedInitOveraligned(); - ~FailedInitOveraligned(); - - void* operator new(size_t, std::align_val_t, float); // Aligned placement - void* operator new[](size_t, std::align_val_t, float); // Aligned placement - void operator delete(void*, std::align_val_t, float); // Aligned placement - void operator delete[](void*, std::align_val_t, float); // Aligned placement -}; - -void TestFailedInit(int n) { - new FailedInit(); - new FailedInit[n]; - new(1.0f) FailedInitOveraligned(); - new(1.0f) FailedInitOveraligned[10]; -} +// semmle-extractor-options: -std=c++17 +typedef unsigned long size_t; +namespace std { + enum class align_val_t : size_t {}; +} + +void* operator new(size_t, float); +void* operator new[](size_t, float); +void* operator new(size_t, std::align_val_t, float); +void* operator new[](size_t, std::align_val_t, float); +void operator delete(void*, float); +void operator delete[](void*, float); +void operator delete(void*, std::align_val_t, float); +void operator delete[](void*, std::align_val_t, float); + +struct String { + String(); + String(const String&); + String(String&&); + String(const char*); + ~String(); + + String& operator=(const String&); + String& operator=(String&&); + + const char* c_str() const; + +private: + const char* p; +}; + +struct SizedDealloc { + char a[32]; + void* operator new(size_t); + void* operator new[](size_t); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); +}; + +struct alignas(128) Overaligned { + char a[256]; +}; + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +void OperatorNew() { + new int; // No constructor + new(1.0f) int; // Placement new, no constructor + new int(); // Zero-init + new String(); // Constructor + new(1.0f) String("hello"); // Placement new, constructor with args + new Overaligned; // Aligned new + new(1.0f) Overaligned(); // Placement aligned new +} + +void OperatorDelete() { + delete static_cast(nullptr); // No destructor + delete static_cast(nullptr); // Non-virtual destructor, with size. + delete static_cast(nullptr); // No destructor, with size. + delete static_cast(nullptr); // No destructor, with size and alignment. + delete static_cast(nullptr); // Virtual destructor + delete static_cast(nullptr); // Pointer to const +} + +void OperatorNewArray(int n) { + new int[n]; // No constructor + new(1.0f) int[n]; // Placement new, no constructor + new String[n]; // Constructor + new Overaligned[n]; // Aligned new + new String[10]; // Constant size +} + +int* const GetPointer(); + +void OperatorDeleteArray() { + delete[] static_cast(nullptr); // No destructor + delete[] static_cast(nullptr); // Non-virtual destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size and alignment. + delete[] static_cast(nullptr); // Virtual destructor + delete[] GetPointer(); +} + +struct FailedInit { + FailedInit(); + ~FailedInit(); + + void* operator new(size_t); // Non-placement + void* operator new[](size_t); // Non-placement + void operator delete(void*, size_t); // Sized deallocation + void operator delete[](void*, size_t); // Sized deallocation +}; + +struct alignas(128) FailedInitOveraligned { + FailedInitOveraligned(); + ~FailedInitOveraligned(); + + void* operator new(size_t, std::align_val_t, float); // Aligned placement + void* operator new[](size_t, std::align_val_t, float); // Aligned placement + void operator delete(void*, std::align_val_t, float); // Aligned placement + void operator delete[](void*, std::align_val_t, float); // Aligned placement +}; + +void TestFailedInit(int n) { + new FailedInit(); + new FailedInit[n]; + new(1.0f) FailedInitOveraligned(); + new(1.0f) FailedInitOveraligned[10]; +} diff --git a/cpp/ql/test/library-tests/calls/calls1.cpp b/cpp/ql/test/library-tests/calls/calls1.cpp index 0c3f1231227b..0b3238b00970 100644 --- a/cpp/ql/test/library-tests/calls/calls1.cpp +++ b/cpp/ql/test/library-tests/calls/calls1.cpp @@ -1,8 +1,8 @@ -#include "calls1.h" - -void swap(int* p, int* q) -{ - int t = *p; - *p = *q; - *q = t; -} +#include "calls1.h" + +void swap(int* p, int* q) +{ + int t = *p; + *p = *q; + *q = t; +} diff --git a/cpp/ql/test/library-tests/calls/calls1.h b/cpp/ql/test/library-tests/calls/calls1.h index 90b150ae9d68..af92b2bded33 100644 --- a/cpp/ql/test/library-tests/calls/calls1.h +++ b/cpp/ql/test/library-tests/calls/calls1.h @@ -1,2 +1,2 @@ - -extern void swap(int*, int*); + +extern void swap(int*, int*); diff --git a/cpp/ql/test/library-tests/calls/calls2.cpp b/cpp/ql/test/library-tests/calls/calls2.cpp index 4f10faa3f208..feedab7f719d 100644 --- a/cpp/ql/test/library-tests/calls/calls2.cpp +++ b/cpp/ql/test/library-tests/calls/calls2.cpp @@ -1,20 +1,20 @@ -#include "calls1.h" - -void g() { - int x = 2; - int y = 4; - swap(&x,&y); -} - -void negate(int& c) { c = -c; } - -template void compute(Iter b, Fct f) -{ - f(b); -} - -void f(int aa) -{ - compute(aa, negate); -} - +#include "calls1.h" + +void g() { + int x = 2; + int y = 4; + swap(&x,&y); +} + +void negate(int& c) { c = -c; } + +template void compute(Iter b, Fct f) +{ + f(b); +} + +void f(int aa) +{ + compute(aa, negate); +} + diff --git a/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp b/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp index 4dafda9752f8..06d239d70cd8 100644 --- a/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp +++ b/cpp/ql/test/library-tests/classes/classes/Classes.ms.cpp @@ -1,41 +1,41 @@ -class A { - -}; - - -class B { - -}; - -class C: A, B { - -}; - -class D: C { - - class E; - -}; - -class D::E { - - class F; - - class G { - public: - /* Non-trivial constructor and destructor */ - G() { 0; } - ~G() { 0; } - }; - -}; - -class D::E::F: D::E::G { - /* Should have generated constructor and destructor, because of D::E::G */ - - static int m() { - D::E::F def; /* Should trigger creation of D::E::F's generated constructor and destructor. */ - } -}; - -// semmle-extractor-options: --microsoft +class A { + +}; + + +class B { + +}; + +class C: A, B { + +}; + +class D: C { + + class E; + +}; + +class D::E { + + class F; + + class G { + public: + /* Non-trivial constructor and destructor */ + G() { 0; } + ~G() { 0; } + }; + +}; + +class D::E::F: D::E::G { + /* Should have generated constructor and destructor, because of D::E::G */ + + static int m() { + D::E::F def; /* Should trigger creation of D::E::F's generated constructor and destructor. */ + } +}; + +// semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp index fed096f45dfd..745a37ca5c85 100644 --- a/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp +++ b/cpp/ql/test/library-tests/classes/derivations/offsets/offsets.cpp @@ -1,84 +1,84 @@ -struct Base { - int b1; - float b1f; -}; - -struct SingleInheritance : Base { - int si1; - float si1f; -}; - -struct Base2 { - int b2; - float b2f; -}; - -struct MultipleInheritance : Base, Base2 { - int mi1; - float mi1f; -}; - -struct DeepInheritance : MultipleInheritance, SingleInheritance { - int di1; - float di1f; -}; - -struct VirtualInheritance1 : virtual Base { - int vi1; - float vi1f; -}; - -struct VirtualInheritance2 : VirtualInheritance1, virtual Base, virtual Base2 { - int vi2; - float vi2f; -}; - -struct EffectivelyVirtual : virtual SingleInheritance, MultipleInheritance { - int ev1; - float ev1f; -}; - -struct PolymorphicBase { - virtual ~PolymorphicBase(); - int pb1; - float pb1f; -}; - -struct InheritsVTable : PolymorphicBase { - int iv1; - float iv1f; -}; - -struct IntroducesVTable : Base { - virtual ~IntroducesVTable(); - int iv2; - float iv2f; -}; - -struct Left : virtual Base { - int l1; - float l1f; -}; - -struct Right : virtual Base { - int r1; - float r1f; -}; - -struct Bottom : Left, Right { - int b1; - float b1f; -}; - -struct DeepSingleInheritance : SingleInheritance { - int dsi1; - float dsi1f; -}; - -struct Incomplete; -Incomplete* p; - -template -struct TemplateClass : Base -{ -}; +struct Base { + int b1; + float b1f; +}; + +struct SingleInheritance : Base { + int si1; + float si1f; +}; + +struct Base2 { + int b2; + float b2f; +}; + +struct MultipleInheritance : Base, Base2 { + int mi1; + float mi1f; +}; + +struct DeepInheritance : MultipleInheritance, SingleInheritance { + int di1; + float di1f; +}; + +struct VirtualInheritance1 : virtual Base { + int vi1; + float vi1f; +}; + +struct VirtualInheritance2 : VirtualInheritance1, virtual Base, virtual Base2 { + int vi2; + float vi2f; +}; + +struct EffectivelyVirtual : virtual SingleInheritance, MultipleInheritance { + int ev1; + float ev1f; +}; + +struct PolymorphicBase { + virtual ~PolymorphicBase(); + int pb1; + float pb1f; +}; + +struct InheritsVTable : PolymorphicBase { + int iv1; + float iv1f; +}; + +struct IntroducesVTable : Base { + virtual ~IntroducesVTable(); + int iv2; + float iv2f; +}; + +struct Left : virtual Base { + int l1; + float l1f; +}; + +struct Right : virtual Base { + int r1; + float r1f; +}; + +struct Bottom : Left, Right { + int b1; + float b1f; +}; + +struct DeepSingleInheritance : SingleInheritance { + int dsi1; + float dsi1f; +}; + +struct Incomplete; +Incomplete* p; + +template +struct TemplateClass : Base +{ +}; diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp index 8345e3a2da61..f07fec46a589 100644 --- a/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/forstmt.cpp @@ -1,102 +1,102 @@ -// GOOD = at least one iteration -// BAD = possibly no iterations - -void test1() { - for (int i = 0; i < 10; i++) { // GOOD - } -} - -void test2() { - for (int i = 0, j = 1; i + j < 10; i++) { // GOOD - } -} - -void test3() { - int j = 2; - for (int i = j = 1; i + j < 10; i++) { // GOOD - } -} - -void test4() { - int i = 2, j = 3; - for (i = j = 1; i + j < 10; i++) { // GOOD - } -} - -void test5() { - int i, k; - for (i = k = 0; i < 10; i++) { // GOOD - } -} - -void test6() { - int i = 0; - for (; i < 10; i++) { // GOOD - i = 1; - } -} - -void test7() { - int i = 0; - for (i = 1; i < 10; i++) { // GOOD - i = 1; - } -} - -void test8() { - int i = 0; - i = 1; - for (; i < 10; i++) { // GOOD (NOT REPORTED) - } -} - -void test9() { - bool done = false; - for (; !done; ) { // GOOD - done = true; - } -} - -void test10(int i) { - bool done = false; - for (; i++; i < 10) { // BAD - for (; !done; ) { // BAD - done = true; - } - } -} - -void test11(int i) { - for (; i++; i < 10) { // BAD - bool done = false; - for (; !done; ) { // GOOD - done = true; - } - } -} - -void test12(int max) { - int i, k; - int max_index = 0; - for (i = k = 0; i < max; i++) { // BAD - max_index = i; - } - for (i = 0; i <= max_index; i++) { // BAD - } -} - -void test13() { - int i; - for (i = 1; i > 0; ) { // GOOD - &i; - } -} - -void test14(bool b) { - int i = 1; - while (b) { - for (; i > 0; ) { // BAD - &i; - } - } +// GOOD = at least one iteration +// BAD = possibly no iterations + +void test1() { + for (int i = 0; i < 10; i++) { // GOOD + } +} + +void test2() { + for (int i = 0, j = 1; i + j < 10; i++) { // GOOD + } +} + +void test3() { + int j = 2; + for (int i = j = 1; i + j < 10; i++) { // GOOD + } +} + +void test4() { + int i = 2, j = 3; + for (i = j = 1; i + j < 10; i++) { // GOOD + } +} + +void test5() { + int i, k; + for (i = k = 0; i < 10; i++) { // GOOD + } +} + +void test6() { + int i = 0; + for (; i < 10; i++) { // GOOD + i = 1; + } +} + +void test7() { + int i = 0; + for (i = 1; i < 10; i++) { // GOOD + i = 1; + } +} + +void test8() { + int i = 0; + i = 1; + for (; i < 10; i++) { // GOOD (NOT REPORTED) + } +} + +void test9() { + bool done = false; + for (; !done; ) { // GOOD + done = true; + } +} + +void test10(int i) { + bool done = false; + for (; i++; i < 10) { // BAD + for (; !done; ) { // BAD + done = true; + } + } +} + +void test11(int i) { + for (; i++; i < 10) { // BAD + bool done = false; + for (; !done; ) { // GOOD + done = true; + } + } +} + +void test12(int max) { + int i, k; + int max_index = 0; + for (i = k = 0; i < max; i++) { // BAD + max_index = i; + } + for (i = 0; i <= max_index; i++) { // BAD + } +} + +void test13() { + int i; + for (i = 1; i > 0; ) { // GOOD + &i; + } +} + +void test14(bool b) { + int i = 1; + while (b) { + for (; i > 0; ) { // BAD + &i; + } + } } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp index e0ddb1b42f15..8155f2fabe91 100644 --- a/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp +++ b/cpp/ql/test/library-tests/controlflow/loopentrycondition/whilestmt.cpp @@ -1,64 +1,64 @@ -// GOOD = at least one iteration -// BAD = possibly no iterations - -void test1() { - bool done = false; - while (!done) { // GOOD - done = true; - } -} - -void test2() { - bool done = true; - done = false; - while (!done) { // GOOD (NOT REPORTED) - done = true; - } -} - -void test3(int i) { - bool done = false; - for (; i++; i < 10) { - while (!done) { // BAD - done = true; - } - } -} - -void test4(int i) { - for (; i++; i < 10) { - bool done = false; - while (!done) { // GOOD - done = true; - } - } -} - -void test5(int max) { - int i = 0, k = 0; - int max_index = 0; - while (i < max) { // BAD - max_index = i; - i++; - } - i = 0; - while (i <= max_index) { // BAD - i++; - } -} - -void test6() { - int i = 1; - while (i > 0) { // GOOD - &i; - } -} - -void test7(bool b) { - int i = 1; - while (b) { // BAD - while (i > 0) { // BAD - &i; - } - } +// GOOD = at least one iteration +// BAD = possibly no iterations + +void test1() { + bool done = false; + while (!done) { // GOOD + done = true; + } +} + +void test2() { + bool done = true; + done = false; + while (!done) { // GOOD (NOT REPORTED) + done = true; + } +} + +void test3(int i) { + bool done = false; + for (; i++; i < 10) { + while (!done) { // BAD + done = true; + } + } +} + +void test4(int i) { + for (; i++; i < 10) { + bool done = false; + while (!done) { // GOOD + done = true; + } + } +} + +void test5(int max) { + int i = 0, k = 0; + int max_index = 0; + while (i < max) { // BAD + max_index = i; + i++; + } + i = 0; + while (i <= max_index) { // BAD + i++; + } +} + +void test6() { + int i = 1; + while (i > 0) { // GOOD + &i; + } +} + +void test7(bool b) { + int i = 1; + while (b) { // BAD + while (i > 0) { // BAD + &i; + } + } } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/conversions/conversions.cpp b/cpp/ql/test/library-tests/conversions/conversions.cpp index 4fb1fa3c200c..b9cdb8df02ad 100644 --- a/cpp/ql/test/library-tests/conversions/conversions.cpp +++ b/cpp/ql/test/library-tests/conversions/conversions.cpp @@ -1,250 +1,250 @@ -typedef unsigned short ushort; - -enum E { - E0, - E1 -}; - -enum class EC : int { - EC0, - EC1 -}; - -void ArithmeticConversions() { - char c = 0; - unsigned char uc = 0; - short s = 0; - unsigned short us = 0; - int i = 0; - unsigned int ui = 0; - long l = 0; - unsigned long ul = 0; - long long ll = 0; - unsigned long long ull = 0; - float f = 0; - double d = 0; - wchar_t wc = 0; - E e{}; - EC ec{}; - - c = uc; - c = (char)uc; - c = char(uc); - c = static_cast(uc); - i = s; - i = (int)s; - i = int(s); - i = static_cast(s); - us = i; - us = (unsigned short)i; - us = ushort(i); - us = static_cast(i); - - i = d; - i = (int)d; - i = int(d); - i = static_cast(d); - - f = c; - f = (float)c; - f = float(c); - f = static_cast(c); - - f = d; - f = (float)d; - f = float(d); - f = static_cast(d); - - d = f; - d = (double)f; - d = double(f); - d = static_cast(f); - - i = E0; - i = e; - i = static_cast(EC::EC0); - i = static_cast(ec); - e = static_cast(i); - ec = static_cast(i); -} - -struct S { - int x; - double y; -}; - -void ConversionsToBool() { - bool b = 0; - int i = 0; - double d = 0; - void* p = nullptr; - int S::* pmd = nullptr; - - if (b) { - } - else if ((bool)b) { - } - else if (i) { - } - else if (d) { - } - else if (p) { - } - else if (pmd) { - } -} - -struct Base { - int b1; - void BaseMethod(); -}; - -struct Middle : Base { - int m1; - void MiddleMethod(); -}; - -struct Derived : Middle { - int d1; - void DerivedMethod(); -}; - -void HierarchyCasts() { - Base b; - Middle m; - Derived d; - - Base* pb = &b; - Middle* pm = &m; - Derived* pd = &d; - - b = m; - b = (Base)m; - b = static_cast(m); - pb = pm; - pb = (Base*)pm; - pb = static_cast(pm); - pb = reinterpret_cast(pm); - - m = (Middle&)b; - m = static_cast(b); - pm = (Middle*)pb; - pm = static_cast(pb); - pm = reinterpret_cast(pb); - - b = d; - b = (Base)d; - b = static_cast(d); - pb = pd; - pb = (Base*)pd; - pb = static_cast(pd); - pb = reinterpret_cast(pd); - - d = (Derived&)b; - d = static_cast(b); - pd = (Derived*)pb; - pd = static_cast(pb); - pd = reinterpret_cast(pb); -} - -void PTMCasts() { - int Base::* pb = &Base::b1; - void (Base::* pmfb)() = &Base::BaseMethod; - int Middle::* pm = &Middle::m1; - void (Middle::* pmfm)() = &Middle::MiddleMethod; - int Derived::* pd = &Derived::d1; - void (Derived::* pmfd)() = &Derived::DerivedMethod; - - pb = (int Base::*)pm; - pmfb = (void (Base::*)())pmfm; - pb = static_cast(pm); - pmfb = static_cast(pmfm); - - pm = pb; - pmfm = pmfb; - pm = (int Middle::*)pb; - pmfm = (void (Middle::*)())pmfb; - pm = static_cast(pb); - pmfm = static_cast(pmfb); - - pb = (int Base::*)pd; - pmfb = (void (Base::*)())pmfd; - pb = static_cast(pd); - pmfb = static_cast(pmfd); - - pd = pb; - pmfd = pmfb; - pd = (int Derived::*)pb; - pmfd = (void (Derived::*)())pmfb; - pd = static_cast(pb); - pmfd = static_cast(pmfb); -} - -struct String { - String(); - String(const String&); - ~String(); -}; - -void Adjust() { - const String& s1 = String(); // prvalue adjustment - Base b; - Derived d; - const Base& rb = true ? b : d; // glvalue adjustment - const Base& r = (Base&)s1; -} - -void QualificationConversions() { - const int* pc = nullptr; - const volatile int* pcv = nullptr; - pcv = pc; - pc = const_cast(pcv); -} - -void PointerIntegralConversions() { - void* p = nullptr; - long n = (long)p; - n = reinterpret_cast(p); - p = (void*)n; - p = reinterpret_cast(n); -} - -struct PolymorphicBase { - virtual ~PolymorphicBase(); -}; - -struct PolymorphicDerived : PolymorphicBase { -}; - -void DynamicCast() { - PolymorphicBase b; - PolymorphicDerived d; - - PolymorphicBase* pb = &b; - PolymorphicDerived* pd = &d; - - // These two casts were previously represented as BaseClassCasts because they were resolved at compile time, but the front-end no longer performs this optimization. - pb = dynamic_cast(pd); - PolymorphicBase& rb = dynamic_cast(d); - - pd = dynamic_cast(pb); - PolymorphicDerived& rd = dynamic_cast(b); -} - -void FuncPtrConversions(int(*pfn)(int), void* p) { - p = (void*)pfn; - pfn = (int(*)(int))p; -} - -int Func(); - -void ConversionsToVoid() { - int x; - (void)x; - static_cast(x); - (void)Func(); - static_cast(Func()); - (void)1; - static_cast(1); -} - +typedef unsigned short ushort; + +enum E { + E0, + E1 +}; + +enum class EC : int { + EC0, + EC1 +}; + +void ArithmeticConversions() { + char c = 0; + unsigned char uc = 0; + short s = 0; + unsigned short us = 0; + int i = 0; + unsigned int ui = 0; + long l = 0; + unsigned long ul = 0; + long long ll = 0; + unsigned long long ull = 0; + float f = 0; + double d = 0; + wchar_t wc = 0; + E e{}; + EC ec{}; + + c = uc; + c = (char)uc; + c = char(uc); + c = static_cast(uc); + i = s; + i = (int)s; + i = int(s); + i = static_cast(s); + us = i; + us = (unsigned short)i; + us = ushort(i); + us = static_cast(i); + + i = d; + i = (int)d; + i = int(d); + i = static_cast(d); + + f = c; + f = (float)c; + f = float(c); + f = static_cast(c); + + f = d; + f = (float)d; + f = float(d); + f = static_cast(d); + + d = f; + d = (double)f; + d = double(f); + d = static_cast(f); + + i = E0; + i = e; + i = static_cast(EC::EC0); + i = static_cast(ec); + e = static_cast(i); + ec = static_cast(i); +} + +struct S { + int x; + double y; +}; + +void ConversionsToBool() { + bool b = 0; + int i = 0; + double d = 0; + void* p = nullptr; + int S::* pmd = nullptr; + + if (b) { + } + else if ((bool)b) { + } + else if (i) { + } + else if (d) { + } + else if (p) { + } + else if (pmd) { + } +} + +struct Base { + int b1; + void BaseMethod(); +}; + +struct Middle : Base { + int m1; + void MiddleMethod(); +}; + +struct Derived : Middle { + int d1; + void DerivedMethod(); +}; + +void HierarchyCasts() { + Base b; + Middle m; + Derived d; + + Base* pb = &b; + Middle* pm = &m; + Derived* pd = &d; + + b = m; + b = (Base)m; + b = static_cast(m); + pb = pm; + pb = (Base*)pm; + pb = static_cast(pm); + pb = reinterpret_cast(pm); + + m = (Middle&)b; + m = static_cast(b); + pm = (Middle*)pb; + pm = static_cast(pb); + pm = reinterpret_cast(pb); + + b = d; + b = (Base)d; + b = static_cast(d); + pb = pd; + pb = (Base*)pd; + pb = static_cast(pd); + pb = reinterpret_cast(pd); + + d = (Derived&)b; + d = static_cast(b); + pd = (Derived*)pb; + pd = static_cast(pb); + pd = reinterpret_cast(pb); +} + +void PTMCasts() { + int Base::* pb = &Base::b1; + void (Base::* pmfb)() = &Base::BaseMethod; + int Middle::* pm = &Middle::m1; + void (Middle::* pmfm)() = &Middle::MiddleMethod; + int Derived::* pd = &Derived::d1; + void (Derived::* pmfd)() = &Derived::DerivedMethod; + + pb = (int Base::*)pm; + pmfb = (void (Base::*)())pmfm; + pb = static_cast(pm); + pmfb = static_cast(pmfm); + + pm = pb; + pmfm = pmfb; + pm = (int Middle::*)pb; + pmfm = (void (Middle::*)())pmfb; + pm = static_cast(pb); + pmfm = static_cast(pmfb); + + pb = (int Base::*)pd; + pmfb = (void (Base::*)())pmfd; + pb = static_cast(pd); + pmfb = static_cast(pmfd); + + pd = pb; + pmfd = pmfb; + pd = (int Derived::*)pb; + pmfd = (void (Derived::*)())pmfb; + pd = static_cast(pb); + pmfd = static_cast(pmfb); +} + +struct String { + String(); + String(const String&); + ~String(); +}; + +void Adjust() { + const String& s1 = String(); // prvalue adjustment + Base b; + Derived d; + const Base& rb = true ? b : d; // glvalue adjustment + const Base& r = (Base&)s1; +} + +void QualificationConversions() { + const int* pc = nullptr; + const volatile int* pcv = nullptr; + pcv = pc; + pc = const_cast(pcv); +} + +void PointerIntegralConversions() { + void* p = nullptr; + long n = (long)p; + n = reinterpret_cast(p); + p = (void*)n; + p = reinterpret_cast(n); +} + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +struct PolymorphicDerived : PolymorphicBase { +}; + +void DynamicCast() { + PolymorphicBase b; + PolymorphicDerived d; + + PolymorphicBase* pb = &b; + PolymorphicDerived* pd = &d; + + // These two casts were previously represented as BaseClassCasts because they were resolved at compile time, but the front-end no longer performs this optimization. + pb = dynamic_cast(pd); + PolymorphicBase& rb = dynamic_cast(d); + + pd = dynamic_cast(pb); + PolymorphicDerived& rd = dynamic_cast(b); +} + +void FuncPtrConversions(int(*pfn)(int), void* p) { + p = (void*)pfn; + pfn = (int(*)(int))p; +} + +int Func(); + +void ConversionsToVoid() { + int x; + (void)x; + static_cast(x); + (void)Func(); + static_cast(Func()); + (void)1; + static_cast(1); +} + diff --git a/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp b/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp index 6d39d6a393cb..13d0e1e4b01b 100644 --- a/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp +++ b/cpp/ql/test/library-tests/ctorinits/ctorinits.cpp @@ -45,53 +45,53 @@ class ArrayMemberInit int xs[4]; }; -struct A { - A(int); -}; - -struct B { - B(int); -}; - -struct C { - C(int); -}; - -struct MultipleBases : A, B, C { - int x; - int y; - int z; - - MultipleBases() : - z(5), - B(1), - x(3), - A(0), - C(2), - y(4) { - } -}; - -struct VB { +struct A { + A(int); +}; + +struct B { + B(int); +}; + +struct C { + C(int); +}; + +struct MultipleBases : A, B, C { + int x; + int y; + int z; + + MultipleBases() : + z(5), + B(1), + x(3), + A(0), + C(2), + y(4) { + } +}; + +struct VB { VB(); VB(int); ~VB(); }; -struct VD : virtual VB { -}; - -struct VirtualAndNonVirtual : VD, VB { - VirtualAndNonVirtual() { - } - ~VirtualAndNonVirtual() { - } -}; - -struct AllYourVirtualBaseAreBelongToUs : VD, VirtualAndNonVirtual, virtual VB { - AllYourVirtualBaseAreBelongToUs() : - VB(5) { - } - ~AllYourVirtualBaseAreBelongToUs() { - } +struct VD : virtual VB { +}; + +struct VirtualAndNonVirtual : VD, VB { + VirtualAndNonVirtual() { + } + ~VirtualAndNonVirtual() { + } +}; + +struct AllYourVirtualBaseAreBelongToUs : VD, VirtualAndNonVirtual, virtual VB { + AllYourVirtualBaseAreBelongToUs() : + VB(5) { + } + ~AllYourVirtualBaseAreBelongToUs() { + } }; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp index 8fc3e31ad72d..3e5111f09bf8 100644 --- a/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp +++ b/cpp/ql/test/library-tests/declarationEntry/template/src5.cpp @@ -1,10 +1,10 @@ -// src5.cpp - -#include "src5.fwd.hpp" - -template - class my_istream { - }; - -template <> - class my_istream; +// src5.cpp + +#include "src5.fwd.hpp" + +template + class my_istream { + }; + +template <> + class my_istream; diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp b/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp index 770189f7266c..8a7cec05bca7 100644 --- a/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp +++ b/cpp/ql/test/library-tests/declarationEntry/template/src5.fwd.hpp @@ -1,8 +1,8 @@ -// src5.fwd.hpp - -#ifndef SRC5_FWD_HPP -#define SRC5_FWD_HPP - - template class my_istream; - -#endif // SRC5_FWD_HPP +// src5.fwd.hpp + +#ifndef SRC5_FWD_HPP +#define SRC5_FWD_HPP + + template class my_istream; + +#endif // SRC5_FWD_HPP diff --git a/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp b/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp index 729a25f50494..2bb70e22f22c 100644 --- a/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp +++ b/cpp/ql/test/library-tests/declarationEntry/template/src6.cpp @@ -1,5 +1,5 @@ -// src6.cpp - -#include "src5.fwd.hpp" - -my_istream *mis_c; +// src6.cpp + +#include "src5.fwd.hpp" + +my_istream *mis_c; diff --git a/cpp/ql/test/library-tests/depends_addressable/addressable.cpp b/cpp/ql/test/library-tests/depends_addressable/addressable.cpp index 40de1850c7ed..a6c22730002a 100644 --- a/cpp/ql/test/library-tests/depends_addressable/addressable.cpp +++ b/cpp/ql/test/library-tests/depends_addressable/addressable.cpp @@ -1,28 +1,28 @@ -class A { -public: - A() {} -}; - -A a; -void f() {} - - -class Test { - A aa; - - void fa() {} - - void test() { - void (*fptr)(); - void (Test::*mfptr)(); - void *ptr; - - ptr = &a; - ptr = &aa; - - fptr = f; // same as below - fptr = &f; // same as above - mfptr = &Test::fa; - - } -}; +class A { +public: + A() {} +}; + +A a; +void f() {} + + +class Test { + A aa; + + void fa() {} + + void test() { + void (*fptr)(); + void (Test::*mfptr)(); + void *ptr; + + ptr = &a; + ptr = &aa; + + fptr = f; // same as below + fptr = &f; // same as above + mfptr = &Test::fa; + + } +}; diff --git a/cpp/ql/test/library-tests/depends_friends/friends.cpp b/cpp/ql/test/library-tests/depends_friends/friends.cpp index 1acdd763a90f..2335b0829e10 100644 --- a/cpp/ql/test/library-tests/depends_friends/friends.cpp +++ b/cpp/ql/test/library-tests/depends_friends/friends.cpp @@ -1,28 +1,28 @@ -class Friend1 { -public: - void f(); -protected: - void g(); -private: - void h(); -}; - -class Friend2 { -public: - void f(); -protected: - void g(); -private: - void h(); -}; - -void Friend2::f() { -} - -void friendFunc() {} - -class C { - friend class Friend1; - friend void Friend2::f(); - friend void friendFunc(); -}; +class Friend1 { +public: + void f(); +protected: + void g(); +private: + void h(); +}; + +class Friend2 { +public: + void f(); +protected: + void g(); +private: + void h(); +}; + +void Friend2::f() { +} + +void friendFunc() {} + +class C { + friend class Friend1; + friend void Friend2::f(); + friend void friendFunc(); +}; diff --git a/cpp/ql/test/library-tests/depends_initializers/initializers.cpp b/cpp/ql/test/library-tests/depends_initializers/initializers.cpp index 079398e20bc3..85a83d245eb8 100644 --- a/cpp/ql/test/library-tests/depends_initializers/initializers.cpp +++ b/cpp/ql/test/library-tests/depends_initializers/initializers.cpp @@ -1,31 +1,31 @@ -//references(UserType) -class A { -public: - A() {} -}; - -int f() { - void *a_ptr = new A(); //match (1 call) - A a = A(); // match (1 call) - return 1; -} - -//calls(Function) -int g() {return 0;} -extern int h(); - -int x = g(); //match -int y = x + g(); //match (1 call, 1 access) -int z = x + g() + h(); //match(2 calls, 1 access) - -//accesses(Variable) -int i = 1; -int j = i; //match (1 access) - -A a; //match(1 call) -A ax = A(); //match (1 call) -A aax = ax; //match (1 access) - -//array initialization -int myIntArray[5] = {i, 0, 0, 0, 0}; //match(1 access) -A myObjectArray[3]; //match(1 call) +//references(UserType) +class A { +public: + A() {} +}; + +int f() { + void *a_ptr = new A(); //match (1 call) + A a = A(); // match (1 call) + return 1; +} + +//calls(Function) +int g() {return 0;} +extern int h(); + +int x = g(); //match +int y = x + g(); //match (1 call, 1 access) +int z = x + g() + h(); //match(2 calls, 1 access) + +//accesses(Variable) +int i = 1; +int j = i; //match (1 access) + +A a; //match(1 call) +A ax = A(); //match (1 call) +A aax = ax; //match (1 access) + +//array initialization +int myIntArray[5] = {i, 0, 0, 0, 0}; //match(1 access) +A myObjectArray[3]; //match(1 call) diff --git a/cpp/ql/test/library-tests/depends_initializers/template_static.cpp b/cpp/ql/test/library-tests/depends_initializers/template_static.cpp index 956cd767d633..4bc6a869fce6 100644 --- a/cpp/ql/test/library-tests/depends_initializers/template_static.cpp +++ b/cpp/ql/test/library-tests/depends_initializers/template_static.cpp @@ -1,89 +1,89 @@ - -const int c = 1; -int v = 1; -int one() {return 1;} - -void myNormalFunction() -{ - static int static_1 = 1; - static int static_c = c; - static int static_v = v; - static int static_one = one(); - int local_1 = 1; - int local_c = c; - int local_v = v; - int local_one = one(); -} - -template void myTemplateFunction() -{ - static int static_int_1 = 1; - static int static_int_c = c; // [initializer is not populated] - static int static_int_v = v; // [initializer is not populated] - static int static_int_one = one(); // [initializer is not populated] - static T static_t_1 = 1; // [initializer is not populated] - static T static_t_c = c; // [initializer is not populated] - static T static_t_v = v; // [initializer is not populated] - static T static_t_one = one(); // [initializer is not populated] - - int local_int_1 = 1; - int local_int_c = c; - int local_int_v = v; - int local_int_one = one(); - T local_t_1 = 1; - T local_t_c = c; - T local_t_v = v; - T local_t_one = one(); -} - -template class myTemplateClass -{ -public: - void myMethod() - { - static int static_int_1 = 1; - static int static_int_c = c; // [initializer is not populated] - static int static_int_v = v; // [initializer is not populated] - static int static_int_one = one(); // [initializer is not populated] - static T static_t_1 = 1; // [initializer is not populated] - static T static_t_c = c; // [initializer is not populated] - static T static_t_v = v; // [initializer is not populated] - static T static_t_one = one(); // [initializer is not populated] - - int local_int_1 = 1; - int local_int_c = c; - int local_int_v = v; - int local_int_one = one(); - T local_t_1 = 1; - T local_t_c = c; - T local_t_v = v; - T local_t_one = one(); - } -}; - -enum myEnum -{ - MYENUM_CONST -}; - -template void myTemplateFunction2(int a = 1, T b = 2) -{ - static int static_int_zero = 0; - static int static_int_ec = MYENUM_CONST; - static int static_int_expr = v + 1; - static int *static_int_addr = &v; - static int static_int_sizeof_v = sizeof(v); - static int static_int_sizeof_t = sizeof(T); - static T static_t_zero = 0; - static T static_t_ec = MYENUM_CONST; - static T static_t_expr = v + 1; - static T *static_t_addr = &v; - static T static_t_sizeof_v = sizeof(v); - static T static_t_sizeof_t = sizeof(T); - - static int static_int_c1 = c; - static int static_int_c2=c; - { - static int static_int_v2 = v; - } -} + +const int c = 1; +int v = 1; +int one() {return 1;} + +void myNormalFunction() +{ + static int static_1 = 1; + static int static_c = c; + static int static_v = v; + static int static_one = one(); + int local_1 = 1; + int local_c = c; + int local_v = v; + int local_one = one(); +} + +template void myTemplateFunction() +{ + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); +} + +template class myTemplateClass +{ +public: + void myMethod() + { + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); + } +}; + +enum myEnum +{ + MYENUM_CONST +}; + +template void myTemplateFunction2(int a = 1, T b = 2) +{ + static int static_int_zero = 0; + static int static_int_ec = MYENUM_CONST; + static int static_int_expr = v + 1; + static int *static_int_addr = &v; + static int static_int_sizeof_v = sizeof(v); + static int static_int_sizeof_t = sizeof(T); + static T static_t_zero = 0; + static T static_t_ec = MYENUM_CONST; + static T static_t_expr = v + 1; + static T *static_t_addr = &v; + static T static_t_sizeof_v = sizeof(v); + static T static_t_sizeof_t = sizeof(T); + + static int static_int_c1 = c; + static int static_int_c2=c; + { + static int static_int_v2 = v; + } +} diff --git a/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp index 06d5ae355fd7..ad703eee4af2 100644 --- a/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp +++ b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp @@ -1,74 +1,74 @@ -namespace ns2 { -const int c = 1; -int v = 1; -int one() {return 1;} - -void myNormalFunction() -{ - static int static_1 = 1; - static int static_c = c; - static int static_v = v; - static int static_one = one(); - int local_1 = 1; - int local_c = c; - int local_v = v; - int local_one = one(); -} - -template void myTemplateFunction() -{ - static int static_int_1 = 1; - static int static_int_c = c; // [initializer is not populated] - static int static_int_v = v; // [initializer is not populated] - static int static_int_one = one(); // [initializer is not populated] - static T static_t_1 = 1; // [initializer is not populated] - static T static_t_c = c; // [initializer is not populated] - static T static_t_v = v; // [initializer is not populated] - static T static_t_one = one(); // [initializer is not populated] - - int local_int_1 = 1; - int local_int_c = c; - int local_int_v = v; - int local_int_one = one(); - T local_t_1 = 1; - T local_t_c = c; - T local_t_v = v; - T local_t_one = one(); -} - -template class myTemplateClass -{ -public: - void myMethod() - { - static int static_int_1 = 1; - static int static_int_c = c; // [initializer is not populated] - static int static_int_v = v; // [initializer is not populated] - static int static_int_one = one(); // [initializer is not populated] - static T static_t_1 = 1; // [initializer is not populated] - static T static_t_c = c; // [initializer is not populated] - static T static_t_v = v; // [initializer is not populated] - static T static_t_one = one(); // [initializer is not populated] - - int local_int_1 = 1; - int local_int_c = c; - int local_int_v = v; - int local_int_one = one(); - T local_t_1 = 1; - T local_t_c = c; - T local_t_v = v; - T local_t_one = one(); - } -}; - -void testFunc() -{ - // instantiate the templates - myTemplateFunction(); - - { - myTemplateClass mtc; - mtc.myMethod(); - } -} -} +namespace ns2 { +const int c = 1; +int v = 1; +int one() {return 1;} + +void myNormalFunction() +{ + static int static_1 = 1; + static int static_c = c; + static int static_v = v; + static int static_one = one(); + int local_1 = 1; + int local_c = c; + int local_v = v; + int local_one = one(); +} + +template void myTemplateFunction() +{ + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); +} + +template class myTemplateClass +{ +public: + void myMethod() + { + static int static_int_1 = 1; + static int static_int_c = c; // [initializer is not populated] + static int static_int_v = v; // [initializer is not populated] + static int static_int_one = one(); // [initializer is not populated] + static T static_t_1 = 1; // [initializer is not populated] + static T static_t_c = c; // [initializer is not populated] + static T static_t_v = v; // [initializer is not populated] + static T static_t_one = one(); // [initializer is not populated] + + int local_int_1 = 1; + int local_int_c = c; + int local_int_v = v; + int local_int_one = one(); + T local_t_1 = 1; + T local_t_c = c; + T local_t_v = v; + T local_t_one = one(); + } +}; + +void testFunc() +{ + // instantiate the templates + myTemplateFunction(); + + { + myTemplateClass mtc; + mtc.myMethod(); + } +} +} diff --git a/cpp/ql/test/library-tests/derived_types/derivedtype.cpp b/cpp/ql/test/library-tests/derived_types/derivedtype.cpp index 496901e6e091..1f71350d5cd4 100644 --- a/cpp/ql/test/library-tests/derived_types/derivedtype.cpp +++ b/cpp/ql/test/library-tests/derived_types/derivedtype.cpp @@ -1,9 +1,9 @@ -class C { - C() {} -}; - -typedef C CC; - -CC** f() { - return 0; -} +class C { + C() {} +}; + +typedef C CC; + +CC** f() { + return 0; +} diff --git a/cpp/ql/test/library-tests/enums/enums/enums.cpp b/cpp/ql/test/library-tests/enums/enums/enums.cpp index 1fb523eca805..8404970fa3a4 100644 --- a/cpp/ql/test/library-tests/enums/enums/enums.cpp +++ b/cpp/ql/test/library-tests/enums/enums/enums.cpp @@ -1,15 +1,15 @@ -const int j = 0; - -enum Day { sun, mon, tue, wed, thu, fri, sat }; -enum Day2 { sun2 = j, mon2, tue2 }; -enum Flag { b = 'a', c = 'b', d = 'd' }; - -Day& operator++(Day& d) -{ - int i = d; - Flag f = Flag(7); - Flag g = Flag(8); - //const int *p = &sat; - Day2 d2 = (Day2)d; - return d = (sat==d) ? sun: Day(d+1); -} +const int j = 0; + +enum Day { sun, mon, tue, wed, thu, fri, sat }; +enum Day2 { sun2 = j, mon2, tue2 }; +enum Flag { b = 'a', c = 'b', d = 'd' }; + +Day& operator++(Day& d) +{ + int i = d; + Flag f = Flag(7); + Flag g = Flag(8); + //const int *p = &sat; + Day2 d2 = (Day2)d; + return d = (sat==d) ? sun: Day(d+1); +} diff --git a/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp b/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp index 0735583e171e..8fd8cb451721 100644 --- a/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp +++ b/cpp/ql/test/library-tests/exprs/comparison_operation/test.cpp @@ -1,14 +1,14 @@ - -int main() -{ - int x; - - if (x == 1) {} - if (x != 1) {} - if (x < 1) {} - if (x > 1) {} - if (x <= 1) {} - if (x >= 1) {} - - return 0; -} + +int main() +{ + int x; + + if (x == 1) {} + if (x != 1) {} + if (x < 1) {} + if (x > 1) {} + if (x <= 1) {} + if (x >= 1) {} + + return 0; +} diff --git a/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp b/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp index 8b33083b13f4..2400527d1747 100644 --- a/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp +++ b/cpp/ql/test/library-tests/exprs/unary_operation/test.cpp @@ -1,15 +1,15 @@ - -int main() -{ - int i; - int *ip; - - i = +(-1); - i++; - ip = &i; - *ip--; - ++i; - --i; - - return 0; -} + +int main() +{ + int i; + int *ip; + + i = +(-1); + i++; + ip = &i; + *ip--; + ++i; + --i; + + return 0; +} diff --git a/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp index 05cd6c16583f..bf0a6176948e 100644 --- a/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp +++ b/cpp/ql/test/library-tests/exprs/value_categories/value_categories.cpp @@ -1,155 +1,155 @@ -int ParamsAndLocals(int x) -{ - int y; - - // y is an lvalue, as is the result of the assignment. x is a load. - y = x + 1; - - // y is a load. - return y; -} - -int Dereference(int* p, int *q) -{ - // *p is an lvalue, as is the result of the assignment. - // p, q, and *q are loads. - *p = *q; - - int x = 5; - - // x is an lvalue. - // *&x is a load. - return *&x; -} - -int& References(int& r) -{ - // The reference r is a load, as is the result of dereferencing the - // reference r. - int x = r; - - // The result of dereferencing the reference r is an lvalue. - // The reference r is a load. - int* p = &r; - - // The result of deferencing the reference r is an lvalue, as is the result - // of the assignment. - // The reference r is a load. - r = 5; - - // The result of dereferencing the reference r is an lvalue. - // The reference r is a load. - return r; -} - -int&& GetRValueRef(); -void CallRValueRef(int&& rr); - -int&& RValueReferences(int&& rr) -{ - // The result of dereferencing the reference returned by GetRValueRef() is - // an xvalue. - CallRValueRef(GetRValueRef()); - - // The result of dereferencing the reference rr is an lvalue, as is the - // result of the assignment. - // The reference rr is a load. - rr = 5; - - // The result of the static cast is an xvalue. The result of dereferencing - // the reference rr is an lvalue. - // The reference rr is a load. - return static_cast(rr); -} - -struct S -{ - int MemberFunction(); -}; - -int CallMemberFunction(S& s) -{ - // The result of dereferencing the reference s is an lvalue. - // The reference s is a load. - return s.MemberFunction(); -} - -int Func(); -void AddressOfFunc() -{ - // Func is a load due to the function-to-function-pointer conversions. - int (*p)() = Func; -} - -struct T -{ - int x; - int y; - int MemberFunc(float); -}; - -void FieldAccesses() -{ - T t; - // t, t.x, and the assignment are all lvalues. - t.x = 0; - // t is an lvalue. - // t.x is a load. - int a = t.x; -} - -void StringLiterals() -{ - // All string literals are lvalues - "String"; - const char* p = "String"; - const char (&a)[7] = "String"; - // The array access is a load - char c = "String"[1]; -} - -void Crement() -{ - int x = 0; - // x is an lvalue. - x++; - // x is an lvalue. - x--; - // x is an lvalue, as is the result of ++x. - ++x; - // x is an lvalue, as is the result of --x. - --x; -} - -void CompoundAssignment() -{ - int x = 0; - - // x is an lvalue, as is the result of x += 1 - x += 1; - // x is an lvalue, as is the result of x -= 1 - x -= 1; - // x is an lvalue, as is the result of x *= 1 - x *= 1; - // x is an lvalue, as is the result of x /= 1 - x /= 1; - // x is an lvalue, as is the result of x %= 1 - x %= 1; - // x is an lvalue, as is the result of x <<= 1 - x <<= 1; - // x is an lvalue, as is the result of x >>= 1 - x >>= 1; - // x is an lvalue, as is the result of x |= 1 - x |= 1; - // x is an lvalue, as is the result of x &= 1 - x &= 1; - // x is an lvalue, as is the result of x ^= 1 - x ^= 1; -} - -void PointerToMemberLiteral() -{ - // All pointer-to-member literals are prvalues - int T::* pmd = &T::x; - int (T::* pmf)(float) = &T::MemberFunc; -} +int ParamsAndLocals(int x) +{ + int y; + + // y is an lvalue, as is the result of the assignment. x is a load. + y = x + 1; + + // y is a load. + return y; +} + +int Dereference(int* p, int *q) +{ + // *p is an lvalue, as is the result of the assignment. + // p, q, and *q are loads. + *p = *q; + + int x = 5; + + // x is an lvalue. + // *&x is a load. + return *&x; +} + +int& References(int& r) +{ + // The reference r is a load, as is the result of dereferencing the + // reference r. + int x = r; + + // The result of dereferencing the reference r is an lvalue. + // The reference r is a load. + int* p = &r; + + // The result of deferencing the reference r is an lvalue, as is the result + // of the assignment. + // The reference r is a load. + r = 5; + + // The result of dereferencing the reference r is an lvalue. + // The reference r is a load. + return r; +} + +int&& GetRValueRef(); +void CallRValueRef(int&& rr); + +int&& RValueReferences(int&& rr) +{ + // The result of dereferencing the reference returned by GetRValueRef() is + // an xvalue. + CallRValueRef(GetRValueRef()); + + // The result of dereferencing the reference rr is an lvalue, as is the + // result of the assignment. + // The reference rr is a load. + rr = 5; + + // The result of the static cast is an xvalue. The result of dereferencing + // the reference rr is an lvalue. + // The reference rr is a load. + return static_cast(rr); +} + +struct S +{ + int MemberFunction(); +}; + +int CallMemberFunction(S& s) +{ + // The result of dereferencing the reference s is an lvalue. + // The reference s is a load. + return s.MemberFunction(); +} + +int Func(); +void AddressOfFunc() +{ + // Func is a load due to the function-to-function-pointer conversions. + int (*p)() = Func; +} + +struct T +{ + int x; + int y; + int MemberFunc(float); +}; + +void FieldAccesses() +{ + T t; + // t, t.x, and the assignment are all lvalues. + t.x = 0; + // t is an lvalue. + // t.x is a load. + int a = t.x; +} + +void StringLiterals() +{ + // All string literals are lvalues + "String"; + const char* p = "String"; + const char (&a)[7] = "String"; + // The array access is a load + char c = "String"[1]; +} + +void Crement() +{ + int x = 0; + // x is an lvalue. + x++; + // x is an lvalue. + x--; + // x is an lvalue, as is the result of ++x. + ++x; + // x is an lvalue, as is the result of --x. + --x; +} + +void CompoundAssignment() +{ + int x = 0; + + // x is an lvalue, as is the result of x += 1 + x += 1; + // x is an lvalue, as is the result of x -= 1 + x -= 1; + // x is an lvalue, as is the result of x *= 1 + x *= 1; + // x is an lvalue, as is the result of x /= 1 + x /= 1; + // x is an lvalue, as is the result of x %= 1 + x %= 1; + // x is an lvalue, as is the result of x <<= 1 + x <<= 1; + // x is an lvalue, as is the result of x >>= 1 + x >>= 1; + // x is an lvalue, as is the result of x |= 1 + x |= 1; + // x is an lvalue, as is the result of x &= 1 + x &= 1; + // x is an lvalue, as is the result of x ^= 1 + x ^= 1; +} + +void PointerToMemberLiteral() +{ + // All pointer-to-member literals are prvalues + int T::* pmd = &T::x; + int (T::* pmf)(float) = &T::MemberFunc; +} diff --git a/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp b/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp index a1190b7955d4..159336371b4d 100644 --- a/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp +++ b/cpp/ql/test/library-tests/exprs_basic/exprs_basic1.cpp @@ -1,33 +1,33 @@ -enum Type { S, I }; - -struct Foo { - char* name; - int count; - char* another_name; - char* yet_another_name; - char initials[2]; - long very_long; -}; - -void create_foo() -{ - Foo xx; - char name[] = "Foo McFoo"; - xx.name = name; - xx.count = 123; - Foo yy = { "Barry McBar", 42, "Baz", "Basildon", { 'B', 'X' }, 5678 }; -} - -void print_foo(Foo* p) -{ - -} - -Foo current_foo; - -Foo set_current_foo(Foo next) -{ - Foo prev = current_foo; - current_foo = next; - return prev; -} +enum Type { S, I }; + +struct Foo { + char* name; + int count; + char* another_name; + char* yet_another_name; + char initials[2]; + long very_long; +}; + +void create_foo() +{ + Foo xx; + char name[] = "Foo McFoo"; + xx.name = name; + xx.count = 123; + Foo yy = { "Barry McBar", 42, "Baz", "Basildon", { 'B', 'X' }, 5678 }; +} + +void print_foo(Foo* p) +{ + +} + +Foo current_foo; + +Foo set_current_foo(Foo next) +{ + Foo prev = current_foo; + current_foo = next; + return prev; +} diff --git a/cpp/ql/test/library-tests/fields/fields/fields.cpp b/cpp/ql/test/library-tests/fields/fields/fields.cpp index c4e26495b4c7..ce65bee0afef 100644 --- a/cpp/ql/test/library-tests/fields/fields/fields.cpp +++ b/cpp/ql/test/library-tests/fields/fields/fields.cpp @@ -1,33 +1,33 @@ -enum Type { S, I }; - -struct Entry { - char* name; - Type t; - char* s; - int i; -private: - int internal; -}; - -class Name { - const char* s; -}; - -class Table { - Name* p; - int sz; -public: - Table(int s=15) { p = new Name[sz=s]; } // constructor - ~Table() { delete[] p; } - Name* lookup (const char*); - bool insert(Name*); -}; - -class Date { - static Table memtbl; - mutable bool cache_valid; -public: - mutable char* cache; - void compute_cache_value() const; -}; - +enum Type { S, I }; + +struct Entry { + char* name; + Type t; + char* s; + int i; +private: + int internal; +}; + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +class Date { + static Table memtbl; + mutable bool cache_valid; +public: + mutable char* cache; + void compute_cache_value() const; +}; + diff --git a/cpp/ql/test/library-tests/files/files1.cpp b/cpp/ql/test/library-tests/files/files1.cpp index cd30b0b78ba9..210c5586e7ac 100644 --- a/cpp/ql/test/library-tests/files/files1.cpp +++ b/cpp/ql/test/library-tests/files/files1.cpp @@ -1,9 +1,9 @@ -#include "files1.h" - - -void swap(int* p, int* q) -{ - int t = *p; - *p = *q; - *q = t; -} +#include "files1.h" + + +void swap(int* p, int* q) +{ + int t = *p; + *p = *q; + *q = t; +} diff --git a/cpp/ql/test/library-tests/files/files1.h b/cpp/ql/test/library-tests/files/files1.h index 90b150ae9d68..af92b2bded33 100644 --- a/cpp/ql/test/library-tests/files/files1.h +++ b/cpp/ql/test/library-tests/files/files1.h @@ -1,2 +1,2 @@ - -extern void swap(int*, int*); + +extern void swap(int*, int*); diff --git a/cpp/ql/test/library-tests/files/files2.cpp b/cpp/ql/test/library-tests/files/files2.cpp index 301b082a5345..6dd7ec111ee4 100644 --- a/cpp/ql/test/library-tests/files/files2.cpp +++ b/cpp/ql/test/library-tests/files/files2.cpp @@ -1,7 +1,7 @@ -#include "files1.h" - -void g() { - int x = 2; - int y = 4; - swap(&x,&y); -} +#include "files1.h" + +void g() { + int x = 2; + int y = 4; + swap(&x,&y); +} diff --git a/cpp/ql/test/library-tests/functions/functions/functions.cpp b/cpp/ql/test/library-tests/functions/functions/functions.cpp index f3c630cebbbb..18b4f6ee5e2b 100644 --- a/cpp/ql/test/library-tests/functions/functions/functions.cpp +++ b/cpp/ql/test/library-tests/functions/functions/functions.cpp @@ -1,33 +1,33 @@ -void f(int a, int b) { - bool c = a==b; -} - -void g(); - -struct A { - void af() { - - } - void ag(); -}; - -void g() { - A a; - a.ag(); -} - -class Name { - const char* s; -}; - -class Table { - Name* p; - int sz; -public: - Table(int s=15) { p = new Name[sz=s]; } // constructor - ~Table() { delete[] p; } - Name* lookup (const char*); - bool insert(Name*); -}; - - +void f(int a, int b) { + bool c = a==b; +} + +void g(); + +struct A { + void af() { + + } + void ag(); +}; + +void g() { + A a; + a.ag(); +} + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + + diff --git a/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp b/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp index 9a176b82b610..107d79da46bf 100644 --- a/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp @@ -1,32 +1,32 @@ -int ReturnConstant() { - return 7; -} - -int ReturnConstantPhi(bool b) { - if (b) { - return 7; - } - else { - return 7; - } -} - -int GetInt(); - -int ReturnNonConstantPhi(bool b) { - if (b) { - return 7; - } - else { - return GetInt(); - } -} - -int ReturnConstantPhiLoop(int x) { - int y = 7; - while (x > 0) { - y = 7; - --x; - } - return y; -} +int ReturnConstant() { + return 7; +} + +int ReturnConstantPhi(bool b) { + if (b) { + return 7; + } + else { + return 7; + } +} + +int GetInt(); + +int ReturnNonConstantPhi(bool b) { + if (b) { + return 7; + } + else { + return GetInt(); + } +} + +int ReturnConstantPhiLoop(int x) { + int y = 7; + while (x > 0) { + y = 7; + --x; + } + return y; +} diff --git a/cpp/ql/test/library-tests/ir/escape/escape.cpp b/cpp/ql/test/library-tests/ir/escape/escape.cpp index 81a23ac4fb21..8a3e57a25d23 100644 --- a/cpp/ql/test/library-tests/ir/escape/escape.cpp +++ b/cpp/ql/test/library-tests/ir/escape/escape.cpp @@ -1,98 +1,98 @@ -void CallByPointer(int* p); -void CallByReference(int& r); - -struct Point { - float x; - float y; - float z; -}; - -struct Base { - float b; -}; - -struct ReusedBase { - float rb; -}; - -struct Intermediate1 : Base, ReusedBase { - float i1; -}; - -struct Intermediate2 : ReusedBase { - float i2; -}; - -struct Derived : Intermediate1, Intermediate2 { - float d; -}; - -void Escape() -{ - int no_result; - int no_; - - no_ = 1; - no_ = no_; - no_result = no_; - no_result = *&no_; -// no_result = (int&)no_; Restore when we have correct IR types for glvalues - no_; - &no_; - no_result = *((&no_) + 0); - no_result = *((&no_) - 0); - no_result = *(0 + &no_); - if (&no_) { - } - while (&no_) { - } - do { - } while (&no_); - for(&no_; &no_; &no_) { - } - - if (&no_ == nullptr) { - } - while (&no_ != nullptr) { - } - - int no_Array[10]; - no_Array; - (int*)no_Array; - no_Array[5]; - 5[no_Array]; - no_result = no_Array[5]; - no_result = 5[no_Array]; - - Point no_Point = { 1, 2, 3 }; - float no_x = no_Point.x; - no_Point.y = no_x; - float no_y = (&no_Point)->y; - (&no_Point)->y = no_y; - float no_z = *(&no_Point.z); - *(&no_Point.z) = no_z; - - Derived no_Derived; - no_Derived.b = 0; - float no_b = no_Derived.b; - no_Derived.i2 = 1; - float no_i2 = no_Derived.i2; - - int no_ssa_addrOf; - int* no_p = &no_ssa_addrOf; - - int no_ssa_refTo; - int& no_r = no_ssa_refTo; - - int no_ssa_refToArrayElement[10]; - int& no_rae = no_ssa_refToArrayElement[5]; - - int no_ssa_refToArray[10]; - int (&no_ra)[10] = no_ssa_refToArray; - - int passByPtr; - CallByPointer(&passByPtr); - - int passByRef; - CallByReference(passByRef); -} +void CallByPointer(int* p); +void CallByReference(int& r); + +struct Point { + float x; + float y; + float z; +}; + +struct Base { + float b; +}; + +struct ReusedBase { + float rb; +}; + +struct Intermediate1 : Base, ReusedBase { + float i1; +}; + +struct Intermediate2 : ReusedBase { + float i2; +}; + +struct Derived : Intermediate1, Intermediate2 { + float d; +}; + +void Escape() +{ + int no_result; + int no_; + + no_ = 1; + no_ = no_; + no_result = no_; + no_result = *&no_; +// no_result = (int&)no_; Restore when we have correct IR types for glvalues + no_; + &no_; + no_result = *((&no_) + 0); + no_result = *((&no_) - 0); + no_result = *(0 + &no_); + if (&no_) { + } + while (&no_) { + } + do { + } while (&no_); + for(&no_; &no_; &no_) { + } + + if (&no_ == nullptr) { + } + while (&no_ != nullptr) { + } + + int no_Array[10]; + no_Array; + (int*)no_Array; + no_Array[5]; + 5[no_Array]; + no_result = no_Array[5]; + no_result = 5[no_Array]; + + Point no_Point = { 1, 2, 3 }; + float no_x = no_Point.x; + no_Point.y = no_x; + float no_y = (&no_Point)->y; + (&no_Point)->y = no_y; + float no_z = *(&no_Point.z); + *(&no_Point.z) = no_z; + + Derived no_Derived; + no_Derived.b = 0; + float no_b = no_Derived.b; + no_Derived.i2 = 1; + float no_i2 = no_Derived.i2; + + int no_ssa_addrOf; + int* no_p = &no_ssa_addrOf; + + int no_ssa_refTo; + int& no_r = no_ssa_refTo; + + int no_ssa_refToArrayElement[10]; + int& no_rae = no_ssa_refToArrayElement[5]; + + int no_ssa_refToArray[10]; + int (&no_ra)[10] = no_ssa_refToArray; + + int passByPtr; + CallByPointer(&passByPtr); + + int passByRef; + CallByReference(passByRef); +} diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 5490cd0f2d97..3ff4c53c17ee 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1,984 +1,984 @@ -void Constants() { - char c_i = 1; - char c_c = 'A'; - - signed char sc_i = -1; - signed char sc_c = 'A'; - - unsigned char uc_i = 5; - unsigned char uc_c = 'A'; - - short s = 5; - unsigned short us = 5; - - int i = 5; - unsigned int ui = 5; - - long l = 5; - unsigned long ul = 5; - - long long ll_i = 5; - long long ll_ll = 5LL; - unsigned long long ull_i = 5; - unsigned long long ull_ull = 5ULL; - - bool b_t = true; - bool b_f = false; - - wchar_t wc_i = 5; - wchar_t wc_c = L'A'; - - char16_t c16 = u'A'; - char32_t c32 = U'A'; - - float f_i = 1; - float f_f = 1.0f; - float f_d = 1.0; - - double d_i = 1; - double d_f = 1.0f; - double d_d = 1.0; -} - -void Foo() { - int x = 5 + 12; - short y = 7; - y = x + y; - x = x * y; -} - -void IntegerOps(int x, int y) { - int z; - - z = x + y; - z = x - y; - z = x * y; - z = x / y; - z = x % y; - - z = x & y; - z = x | y; - z = x ^ y; - - z = x << y; - z = x >> y; - - z = x; - - z += x; - z -= x; - z *= x; - z /= x; - z %= x; - - z &= x; - z |= x; - z ^= x; - - z <<= x; - z >>= x; - - z = +x; - z = -x; - z = ~x; - z = !x; -} - -void IntegerCompare(int x, int y) { - bool b; - - b = x == y; - b = x != y; - b = x < y; - b = x > y; - b = x <= y; - b = x >= y; -} - -void IntegerCrement(int x) { - int y; - - y = ++x; - y = --x; - y = x++; - y = x--; -} - -void IntegerCrement_LValue(int x) { - int* p; - - p = &(++x); - p = &(--x); -} - -void FloatOps(double x, double y) { - double z; - - z = x + y; - z = x - y; - z = x * y; - z = x / y; - - z = x; - - z += x; - z -= x; - z *= x; - z /= x; - - z = +x; - z = -x; -} - -void FloatCompare(double x, double y) { - bool b; - - b = x == y; - b = x != y; - b = x < y; - b = x > y; - b = x <= y; - b = x >= y; -} - -void FloatCrement(float x) { - float y; - - y = ++x; - y = --x; - y = x++; - y = x--; -} - -void PointerOps(int* p, int i) { - int* q; - bool b; - - q = p + i; - q = i + p; - q = p - i; - i = p - q; - - q = p; - - q += i; - q -= i; - - b = p; - b = !p; -} - -void ArrayAccess(int* p, int i) { - int x; - - x = p[i]; - x = i[p]; - - p[i] = x; - i[p] = x; - - int a[10]; - x = a[i]; - x = i[a]; - a[i] = x; - i[a] = x; -} - -void StringLiteral(int i) { - char c = "Foo"[i]; - wchar_t* pwc = L"Bar"; - wchar_t wc = pwc[i]; -} - -void PointerCompare(int* p, int* q) { - bool b; - - b = p == q; - b = p != q; - b = p < q; - b = p > q; - b = p <= q; - b = p >= q; -} - -void PointerCrement(int* p) { - int* q; - - q = ++p; - q = --p; - q = p++; - q = p--; -} - -void CompoundAssignment() { - // No conversion necessary - int x = 5; - x += 7; - - // Left side is converted to 'int' - short y = 5; - y += x; - - // Technically the left side is promoted to int, but we don't model that - y <<= 1; - - // Left side is converted to 'float' - long z = 7; - z += 2.0f; -} - -void UninitializedVariables() { - int x; - int y = x; -} - -int Parameters(int x, int y) { - return x % y; -} - -void IfStatements(bool b, int x, int y) { - if (b) { - } - - if (b) { - x = y; - } - - if (x < 7) - x = 2; - else - x = 7; -} - -void WhileStatements(int n) { - while (n > 0) { - n -= 1; - } -} - -void DoStatements(int n) { - do { - n -= 1; - } while (n > 0); -} - -void For_Empty() { - int j; - for (;;) { - ; - } -} - -void For_Init() { - for (int i = 0;;) { - ; - } -} - -void For_Condition() { - int i = 0; - for (; i < 10;) { - ; - } -} - -void For_Update() { - int i = 0; - for (;; i += 1) { - ; - } -} - -void For_InitCondition() { - for (int i = 0; i < 10;) { - ; - } -} - -void For_InitUpdate() { - for (int i = 0;; i += 1) { - ; - } -} - -void For_ConditionUpdate() { - int i = 0; - for (; i < 10; i += 1) { - ; - } -} - -void For_InitConditionUpdate() { - for (int i = 0; i < 10; i += 1) { - ; - } -} - -void For_Break() { - for (int i = 0; i < 10; i += 1) { - if (i == 5) { - break; - } - } -} - -void For_Continue_Update() { - for (int i = 0; i < 10; i += 1) { - if (i == 5) { - continue; - } - } -} - -void For_Continue_NoUpdate() { - for (int i = 0; i < 10;) { - if (i == 5) { - continue; - } - } -} - -int Dereference(int* p) { - *p = 1; - return *p; -} - -int g; - -int* AddressOf() { - return &g; -} - -void Break(int n) { - while (n > 0) { - if (n == 1) - break; - n -= 1; - } -} - -void Continue(int n) { - do { - if (n == 1) { - continue; - } - n -= 1; - } while (n > 0); -} - -void VoidFunc(); -int Add(int x, int y); - -void Call() { - VoidFunc(); -} - -int CallAdd(int x, int y) { - return Add(x, y); -} - -int Comma(int x, int y) { - return VoidFunc(), CallAdd(x, y); -} - -void Switch(int x) { - int y; - switch (x) { - y = 1234; - - case -1: - y = -1; - break; - - case 1: - case 2: - y = 1; - break; - - case 3: - y = 3; - case 4: - y = 4; - break; - - default: - y = 0; - break; - - y = 5678; - } -} - -struct Point { - int x; - int y; -}; - -struct Rect { - Point topLeft; - Point bottomRight; -}; - -Point ReturnStruct(Point pt) { - return pt; -} - -void FieldAccess() { - Point pt; - pt.x = 5; - pt.y = pt.x; - int* p = &pt.y; -} - -void LogicalOr(bool a, bool b) { - int x; - if (a || b) { - x = 7; - } - - if (a || b) { - x = 1; - } - else { - x = 5; - } -} - -void LogicalAnd(bool a, bool b) { - int x; - if (a && b) { - x = 7; - } - - if (a && b) { - x = 1; - } - else { - x = 5; - } -} - -void LogicalNot(bool a, bool b) { - int x; - if (!a) { - x = 1; - } - - if (!(a && b)) { - x = 2; - } - else { - x = 3; - } -} - -void ConditionValues(bool a, bool b) { - bool x; - x = a && b; - x = a || b; - x = !(a || b); -} - -void Conditional(bool a, int x, int y) { - int z = a ? x : y; -} - -void Conditional_LValue(bool a) { - int x; - int y; - (a ? x : y) = 5; -} - -void Conditional_Void(bool a) { - a ? VoidFunc() : VoidFunc(); -} - -void Nullptr() { - int* p = nullptr; - int* q = 0; - p = nullptr; - q = 0; -} - -void InitList(int x, float f) { - Point pt1 = { x, f }; - Point pt2 = { x }; - Point pt3 = {}; - - int x1 = { 1 }; - int x2 = {}; -} - -void NestedInitList(int x, float f) { - Rect r1 = {}; - Rect r2 = { { x, f } }; - Rect r3 = { { x, f }, { x, f } }; - Rect r4 = { { x }, { x } }; -} - -void ArrayInit(int x, float f) { - int a1[3] = {}; - int a2[3] = { x, f, 0 }; - int a3[3] = { x }; -} - -union U { - double d; - int i; -}; - -void UnionInit(int x, float f) { - U u1 = { f }; -// U u2 = {}; Waiting for fix -} - -void EarlyReturn(int x, int y) { - if (x < y) { - return; - } - - y = x; -} - -int EarlyReturnValue(int x, int y) { - if (x < y) { - return x; - } - - return x + y; -} - -int CallViaFuncPtr(int (*pfn)(int)) { - return pfn(5); -} - -typedef enum { - E_0, - E_1 -} E; - -int EnumSwitch(E e) { - switch (e) { - case E_0: - return 0; - case E_1: - return 1; - default: - return -1; - } -} - -void InitArray() { - char a_pad[32] = ""; - char a_nopad[4] = "foo"; - char a_infer[] = "blah"; - char b[2]; - char c[2] = {}; - char d[2] = { 0 }; - char e[2] = { 0, 1 }; - char f[3] = { 0 }; -} - -void VarArgFunction(const char* s, ...); - -void VarArgs() { - VarArgFunction("%d %s", 1, "string"); -} - -int FuncPtrTarget(int); - -void SetFuncPtr() { - int (*pfn)(int) = FuncPtrTarget; - pfn = &FuncPtrTarget; - pfn = *FuncPtrTarget; - pfn = ***&FuncPtrTarget; -} - -struct String { - String(); - String(const String&); - String(String&&); - String(const char*); - ~String(); - - String& operator=(const String&); - String& operator=(String&&); - - const char* c_str() const; - -private: - const char* p; -}; - -String ReturnObject(); - -void DeclareObject() { - String s1; - String s2("hello"); - String s3 = ReturnObject(); - String s4 = String("test"); -} - -void CallMethods(String& r, String* p, String s) { - r.c_str(); - p->c_str(); - s.c_str(); -} - -class C { -public: - static int StaticMemberFunction(int x) { - return x; - } - - int InstanceMemberFunction(int x) { - return x; - } - - virtual int VirtualMemberFunction(int x) { - return x; - } - - void FieldAccess() { - this->m_a = 0; - (*this).m_a = 1; - m_a = 2; - int x; - x = this->m_a; - x = (*this).m_a; - x = m_a; - } - - void MethodCalls() { - this->InstanceMemberFunction(0); - (*this).InstanceMemberFunction(1); - InstanceMemberFunction(2); - } - - C() : - m_a(1), - m_c(3), - m_e{}, - m_f("test") - { - } - -private: - int m_a; - String m_b; - char m_c; - float m_d; - void* m_e; - String m_f; -}; - -int DerefReference(int& r) { - return r; -} - -int& TakeReference() { - return g; -} - -String& ReturnReference(); - -void InitReference(int x) { - int& r = x; - int& r2 = r; - const String& r3 = ReturnReference(); -} - -void ArrayReferences() { - int a[10]; - int (&ra)[10] = a; - int x = ra[5]; -} - -void FunctionReferences() { - int(&rfn)(int) = FuncPtrTarget; - int(*pfn)(int) = rfn; - rfn(5); -} - -template -T min(T x, T y) { - return (x < y) ? x : y; -} - -int CallMin(int x, int y) { - return min(x, y); -} - -template -struct Outer { - template - static T Func(U x, V y) { - return T(); - } -}; - -double CallNestedTemplateFunc() { - return Outer::Func(nullptr, 'o'); -} - -void TryCatch(bool b) { - try { - int x = 5; - if (b) { - throw "string literal"; - } - else if (x < 2) { - x = b ? 7 : throw String("String object"); - } - x = 7; - } - catch (const char* s) { - throw String(s); - } - catch (const String& e) { - } - catch (...) { - throw; - } -} - -struct Base { - String base_s; - - Base() { - } - ~Base() { - } -}; - -struct Middle : Base { - String middle_s; - - Middle() { - } - ~Middle() { - } -}; - -struct Derived : Middle { - String derived_s; - - Derived() { - } - ~Derived() { - } -}; - -struct MiddleVB1 : virtual Base { - String middlevb1_s; - - MiddleVB1() { - } - ~MiddleVB1() { - } -}; - -struct MiddleVB2 : virtual Base { - String middlevb2_s; - - MiddleVB2() { - } - ~MiddleVB2() { - } -}; - -struct DerivedVB : MiddleVB1, MiddleVB2 { - String derivedvb_s; - - DerivedVB() { - } - ~DerivedVB() { - } -}; - -void HierarchyConversions() { - Base b; - Middle m; - Derived d; - - Base* pb = &b; - Middle* pm = &m; - Derived* pd = &d; - - b = m; - b = (Base)m; - b = static_cast(m); - pb = pm; - pb = (Base*)pm; - pb = static_cast(pm); - pb = reinterpret_cast(pm); - - m = (Middle&)b; - m = static_cast(b); - pm = (Middle*)pb; - pm = static_cast(pb); - pm = reinterpret_cast(pb); - - b = d; - b = (Base)d; - b = static_cast(d); - pb = pd; - pb = (Base*)pd; - pb = static_cast(pd); - pb = reinterpret_cast(pd); - - d = (Derived&)b; - d = static_cast(b); - pd = (Derived*)pb; - pd = static_cast(pb); - pd = reinterpret_cast(pb); - - MiddleVB1* pmv = nullptr; - DerivedVB* pdv = nullptr; - pb = pmv; - pb = pdv; -} - -struct PolymorphicBase { - virtual ~PolymorphicBase(); -}; - -struct PolymorphicDerived : PolymorphicBase { -}; - -void DynamicCast() { - PolymorphicBase b; - PolymorphicDerived d; - - PolymorphicBase* pb = &b; - PolymorphicDerived* pd = &d; - - // These two casts are represented as BaseClassCasts because they can be resolved at compile time. - pb = dynamic_cast(pd); - PolymorphicBase& rb = dynamic_cast(d); - - pd = dynamic_cast(pb); - PolymorphicDerived& rd = dynamic_cast(b); - - void* pv = dynamic_cast(pb); - const void* pcv = dynamic_cast(pd); -} - -String::String() : - String("") { // Delegating constructor call -} - -void ArrayConversions() { - char a[5]; - const char* p = a; - p = "test"; - p = &a[0]; - p = &"test"[0]; - char (&ra)[5] = a; - const char (&rs)[5] = "test"; - const char (*pa)[5] = &a; - pa = &"test"; -} - -void FuncPtrConversions(int(*pfn)(int), void* p) { - p = (void*)pfn; - pfn = (int(*)(int))p; -} - -void VarArgUsage(int x, ...) { - __builtin_va_list args; - - __builtin_va_start(args, x); - __builtin_va_list args2; - __builtin_va_start(args2, args); - double d = __builtin_va_arg(args, double); - float f = __builtin_va_arg(args, float); - __builtin_va_end(args); - __builtin_va_end(args2); -} - -void CastToVoid(int x) { - (void)x; -} - -void ConstantConditions(int x) { - bool a = true && true; - int b = (true) ? x : x; -} - -typedef unsigned long size_t; - -namespace std { - enum class align_val_t : size_t {}; -} - -void* operator new(size_t, float); -void* operator new[](size_t, float); -void* operator new(size_t, std::align_val_t, float); -void* operator new[](size_t, std::align_val_t, float); -void operator delete(void*, float); -void operator delete[](void*, float); -void operator delete(void*, std::align_val_t, float); -void operator delete[](void*, std::align_val_t, float); - -struct SizedDealloc { - char a[32]; - void* operator new(size_t); - void* operator new[](size_t); - void operator delete(void*, size_t); - void operator delete[](void*, size_t); -}; - -struct alignas(128) Overaligned { - char a[256]; -}; - -struct DefaultCtorWithDefaultParam { - DefaultCtorWithDefaultParam(double d = 1.0); -}; - -void OperatorNew() { - new int; // No constructor - new(1.0f) int; // Placement new, no constructor - new int(); // Zero-init - new String(); // Constructor - new(1.0f) String("hello"); // Placement new, constructor with args - new Overaligned; // Aligned new - new(1.0f) Overaligned(); // Placement aligned new with zero-init -} - -void OperatorNewArray(int n) { - new int[10]; // Constant size - new int[n]; // No constructor - new(1.0f) int[n]; // Placement new, no constructor - new String[n]; // Constructor - new Overaligned[n]; // Aligned new - new(1.0f) Overaligned[10]; // Aligned placement new - new DefaultCtorWithDefaultParam[n]; - new int[n] { 0, 1, 2 }; -} - -int designatedInit() { - int a1[1000] = { [2] = 10002, [900] = 10900 }; - return a1[900]; -} - -#if 0 -void OperatorDelete() { - delete static_cast(nullptr); // No destructor - delete static_cast(nullptr); // Non-virtual destructor, with size. - delete static_cast(nullptr); // No destructor, with size. - delete static_cast(nullptr); // No destructor, with size and alignment. - delete static_cast(nullptr); // Virtual destructor -} - -void OperatorDeleteArray() { - delete[] static_cast(nullptr); // No destructor - delete[] static_cast(nullptr); // Non-virtual destructor, with size. - delete[] static_cast(nullptr); // No destructor, with size. - delete[] static_cast(nullptr); // No destructor, with size and alignment. - delete[] static_cast(nullptr); // Virtual destructor -} -#endif - -// semmle-extractor-options: -std=c++17 +void Constants() { + char c_i = 1; + char c_c = 'A'; + + signed char sc_i = -1; + signed char sc_c = 'A'; + + unsigned char uc_i = 5; + unsigned char uc_c = 'A'; + + short s = 5; + unsigned short us = 5; + + int i = 5; + unsigned int ui = 5; + + long l = 5; + unsigned long ul = 5; + + long long ll_i = 5; + long long ll_ll = 5LL; + unsigned long long ull_i = 5; + unsigned long long ull_ull = 5ULL; + + bool b_t = true; + bool b_f = false; + + wchar_t wc_i = 5; + wchar_t wc_c = L'A'; + + char16_t c16 = u'A'; + char32_t c32 = U'A'; + + float f_i = 1; + float f_f = 1.0f; + float f_d = 1.0; + + double d_i = 1; + double d_f = 1.0f; + double d_d = 1.0; +} + +void Foo() { + int x = 5 + 12; + short y = 7; + y = x + y; + x = x * y; +} + +void IntegerOps(int x, int y) { + int z; + + z = x + y; + z = x - y; + z = x * y; + z = x / y; + z = x % y; + + z = x & y; + z = x | y; + z = x ^ y; + + z = x << y; + z = x >> y; + + z = x; + + z += x; + z -= x; + z *= x; + z /= x; + z %= x; + + z &= x; + z |= x; + z ^= x; + + z <<= x; + z >>= x; + + z = +x; + z = -x; + z = ~x; + z = !x; +} + +void IntegerCompare(int x, int y) { + bool b; + + b = x == y; + b = x != y; + b = x < y; + b = x > y; + b = x <= y; + b = x >= y; +} + +void IntegerCrement(int x) { + int y; + + y = ++x; + y = --x; + y = x++; + y = x--; +} + +void IntegerCrement_LValue(int x) { + int* p; + + p = &(++x); + p = &(--x); +} + +void FloatOps(double x, double y) { + double z; + + z = x + y; + z = x - y; + z = x * y; + z = x / y; + + z = x; + + z += x; + z -= x; + z *= x; + z /= x; + + z = +x; + z = -x; +} + +void FloatCompare(double x, double y) { + bool b; + + b = x == y; + b = x != y; + b = x < y; + b = x > y; + b = x <= y; + b = x >= y; +} + +void FloatCrement(float x) { + float y; + + y = ++x; + y = --x; + y = x++; + y = x--; +} + +void PointerOps(int* p, int i) { + int* q; + bool b; + + q = p + i; + q = i + p; + q = p - i; + i = p - q; + + q = p; + + q += i; + q -= i; + + b = p; + b = !p; +} + +void ArrayAccess(int* p, int i) { + int x; + + x = p[i]; + x = i[p]; + + p[i] = x; + i[p] = x; + + int a[10]; + x = a[i]; + x = i[a]; + a[i] = x; + i[a] = x; +} + +void StringLiteral(int i) { + char c = "Foo"[i]; + wchar_t* pwc = L"Bar"; + wchar_t wc = pwc[i]; +} + +void PointerCompare(int* p, int* q) { + bool b; + + b = p == q; + b = p != q; + b = p < q; + b = p > q; + b = p <= q; + b = p >= q; +} + +void PointerCrement(int* p) { + int* q; + + q = ++p; + q = --p; + q = p++; + q = p--; +} + +void CompoundAssignment() { + // No conversion necessary + int x = 5; + x += 7; + + // Left side is converted to 'int' + short y = 5; + y += x; + + // Technically the left side is promoted to int, but we don't model that + y <<= 1; + + // Left side is converted to 'float' + long z = 7; + z += 2.0f; +} + +void UninitializedVariables() { + int x; + int y = x; +} + +int Parameters(int x, int y) { + return x % y; +} + +void IfStatements(bool b, int x, int y) { + if (b) { + } + + if (b) { + x = y; + } + + if (x < 7) + x = 2; + else + x = 7; +} + +void WhileStatements(int n) { + while (n > 0) { + n -= 1; + } +} + +void DoStatements(int n) { + do { + n -= 1; + } while (n > 0); +} + +void For_Empty() { + int j; + for (;;) { + ; + } +} + +void For_Init() { + for (int i = 0;;) { + ; + } +} + +void For_Condition() { + int i = 0; + for (; i < 10;) { + ; + } +} + +void For_Update() { + int i = 0; + for (;; i += 1) { + ; + } +} + +void For_InitCondition() { + for (int i = 0; i < 10;) { + ; + } +} + +void For_InitUpdate() { + for (int i = 0;; i += 1) { + ; + } +} + +void For_ConditionUpdate() { + int i = 0; + for (; i < 10; i += 1) { + ; + } +} + +void For_InitConditionUpdate() { + for (int i = 0; i < 10; i += 1) { + ; + } +} + +void For_Break() { + for (int i = 0; i < 10; i += 1) { + if (i == 5) { + break; + } + } +} + +void For_Continue_Update() { + for (int i = 0; i < 10; i += 1) { + if (i == 5) { + continue; + } + } +} + +void For_Continue_NoUpdate() { + for (int i = 0; i < 10;) { + if (i == 5) { + continue; + } + } +} + +int Dereference(int* p) { + *p = 1; + return *p; +} + +int g; + +int* AddressOf() { + return &g; +} + +void Break(int n) { + while (n > 0) { + if (n == 1) + break; + n -= 1; + } +} + +void Continue(int n) { + do { + if (n == 1) { + continue; + } + n -= 1; + } while (n > 0); +} + +void VoidFunc(); +int Add(int x, int y); + +void Call() { + VoidFunc(); +} + +int CallAdd(int x, int y) { + return Add(x, y); +} + +int Comma(int x, int y) { + return VoidFunc(), CallAdd(x, y); +} + +void Switch(int x) { + int y; + switch (x) { + y = 1234; + + case -1: + y = -1; + break; + + case 1: + case 2: + y = 1; + break; + + case 3: + y = 3; + case 4: + y = 4; + break; + + default: + y = 0; + break; + + y = 5678; + } +} + +struct Point { + int x; + int y; +}; + +struct Rect { + Point topLeft; + Point bottomRight; +}; + +Point ReturnStruct(Point pt) { + return pt; +} + +void FieldAccess() { + Point pt; + pt.x = 5; + pt.y = pt.x; + int* p = &pt.y; +} + +void LogicalOr(bool a, bool b) { + int x; + if (a || b) { + x = 7; + } + + if (a || b) { + x = 1; + } + else { + x = 5; + } +} + +void LogicalAnd(bool a, bool b) { + int x; + if (a && b) { + x = 7; + } + + if (a && b) { + x = 1; + } + else { + x = 5; + } +} + +void LogicalNot(bool a, bool b) { + int x; + if (!a) { + x = 1; + } + + if (!(a && b)) { + x = 2; + } + else { + x = 3; + } +} + +void ConditionValues(bool a, bool b) { + bool x; + x = a && b; + x = a || b; + x = !(a || b); +} + +void Conditional(bool a, int x, int y) { + int z = a ? x : y; +} + +void Conditional_LValue(bool a) { + int x; + int y; + (a ? x : y) = 5; +} + +void Conditional_Void(bool a) { + a ? VoidFunc() : VoidFunc(); +} + +void Nullptr() { + int* p = nullptr; + int* q = 0; + p = nullptr; + q = 0; +} + +void InitList(int x, float f) { + Point pt1 = { x, f }; + Point pt2 = { x }; + Point pt3 = {}; + + int x1 = { 1 }; + int x2 = {}; +} + +void NestedInitList(int x, float f) { + Rect r1 = {}; + Rect r2 = { { x, f } }; + Rect r3 = { { x, f }, { x, f } }; + Rect r4 = { { x }, { x } }; +} + +void ArrayInit(int x, float f) { + int a1[3] = {}; + int a2[3] = { x, f, 0 }; + int a3[3] = { x }; +} + +union U { + double d; + int i; +}; + +void UnionInit(int x, float f) { + U u1 = { f }; +// U u2 = {}; Waiting for fix +} + +void EarlyReturn(int x, int y) { + if (x < y) { + return; + } + + y = x; +} + +int EarlyReturnValue(int x, int y) { + if (x < y) { + return x; + } + + return x + y; +} + +int CallViaFuncPtr(int (*pfn)(int)) { + return pfn(5); +} + +typedef enum { + E_0, + E_1 +} E; + +int EnumSwitch(E e) { + switch (e) { + case E_0: + return 0; + case E_1: + return 1; + default: + return -1; + } +} + +void InitArray() { + char a_pad[32] = ""; + char a_nopad[4] = "foo"; + char a_infer[] = "blah"; + char b[2]; + char c[2] = {}; + char d[2] = { 0 }; + char e[2] = { 0, 1 }; + char f[3] = { 0 }; +} + +void VarArgFunction(const char* s, ...); + +void VarArgs() { + VarArgFunction("%d %s", 1, "string"); +} + +int FuncPtrTarget(int); + +void SetFuncPtr() { + int (*pfn)(int) = FuncPtrTarget; + pfn = &FuncPtrTarget; + pfn = *FuncPtrTarget; + pfn = ***&FuncPtrTarget; +} + +struct String { + String(); + String(const String&); + String(String&&); + String(const char*); + ~String(); + + String& operator=(const String&); + String& operator=(String&&); + + const char* c_str() const; + +private: + const char* p; +}; + +String ReturnObject(); + +void DeclareObject() { + String s1; + String s2("hello"); + String s3 = ReturnObject(); + String s4 = String("test"); +} + +void CallMethods(String& r, String* p, String s) { + r.c_str(); + p->c_str(); + s.c_str(); +} + +class C { +public: + static int StaticMemberFunction(int x) { + return x; + } + + int InstanceMemberFunction(int x) { + return x; + } + + virtual int VirtualMemberFunction(int x) { + return x; + } + + void FieldAccess() { + this->m_a = 0; + (*this).m_a = 1; + m_a = 2; + int x; + x = this->m_a; + x = (*this).m_a; + x = m_a; + } + + void MethodCalls() { + this->InstanceMemberFunction(0); + (*this).InstanceMemberFunction(1); + InstanceMemberFunction(2); + } + + C() : + m_a(1), + m_c(3), + m_e{}, + m_f("test") + { + } + +private: + int m_a; + String m_b; + char m_c; + float m_d; + void* m_e; + String m_f; +}; + +int DerefReference(int& r) { + return r; +} + +int& TakeReference() { + return g; +} + +String& ReturnReference(); + +void InitReference(int x) { + int& r = x; + int& r2 = r; + const String& r3 = ReturnReference(); +} + +void ArrayReferences() { + int a[10]; + int (&ra)[10] = a; + int x = ra[5]; +} + +void FunctionReferences() { + int(&rfn)(int) = FuncPtrTarget; + int(*pfn)(int) = rfn; + rfn(5); +} + +template +T min(T x, T y) { + return (x < y) ? x : y; +} + +int CallMin(int x, int y) { + return min(x, y); +} + +template +struct Outer { + template + static T Func(U x, V y) { + return T(); + } +}; + +double CallNestedTemplateFunc() { + return Outer::Func(nullptr, 'o'); +} + +void TryCatch(bool b) { + try { + int x = 5; + if (b) { + throw "string literal"; + } + else if (x < 2) { + x = b ? 7 : throw String("String object"); + } + x = 7; + } + catch (const char* s) { + throw String(s); + } + catch (const String& e) { + } + catch (...) { + throw; + } +} + +struct Base { + String base_s; + + Base() { + } + ~Base() { + } +}; + +struct Middle : Base { + String middle_s; + + Middle() { + } + ~Middle() { + } +}; + +struct Derived : Middle { + String derived_s; + + Derived() { + } + ~Derived() { + } +}; + +struct MiddleVB1 : virtual Base { + String middlevb1_s; + + MiddleVB1() { + } + ~MiddleVB1() { + } +}; + +struct MiddleVB2 : virtual Base { + String middlevb2_s; + + MiddleVB2() { + } + ~MiddleVB2() { + } +}; + +struct DerivedVB : MiddleVB1, MiddleVB2 { + String derivedvb_s; + + DerivedVB() { + } + ~DerivedVB() { + } +}; + +void HierarchyConversions() { + Base b; + Middle m; + Derived d; + + Base* pb = &b; + Middle* pm = &m; + Derived* pd = &d; + + b = m; + b = (Base)m; + b = static_cast(m); + pb = pm; + pb = (Base*)pm; + pb = static_cast(pm); + pb = reinterpret_cast(pm); + + m = (Middle&)b; + m = static_cast(b); + pm = (Middle*)pb; + pm = static_cast(pb); + pm = reinterpret_cast(pb); + + b = d; + b = (Base)d; + b = static_cast(d); + pb = pd; + pb = (Base*)pd; + pb = static_cast(pd); + pb = reinterpret_cast(pd); + + d = (Derived&)b; + d = static_cast(b); + pd = (Derived*)pb; + pd = static_cast(pb); + pd = reinterpret_cast(pb); + + MiddleVB1* pmv = nullptr; + DerivedVB* pdv = nullptr; + pb = pmv; + pb = pdv; +} + +struct PolymorphicBase { + virtual ~PolymorphicBase(); +}; + +struct PolymorphicDerived : PolymorphicBase { +}; + +void DynamicCast() { + PolymorphicBase b; + PolymorphicDerived d; + + PolymorphicBase* pb = &b; + PolymorphicDerived* pd = &d; + + // These two casts are represented as BaseClassCasts because they can be resolved at compile time. + pb = dynamic_cast(pd); + PolymorphicBase& rb = dynamic_cast(d); + + pd = dynamic_cast(pb); + PolymorphicDerived& rd = dynamic_cast(b); + + void* pv = dynamic_cast(pb); + const void* pcv = dynamic_cast(pd); +} + +String::String() : + String("") { // Delegating constructor call +} + +void ArrayConversions() { + char a[5]; + const char* p = a; + p = "test"; + p = &a[0]; + p = &"test"[0]; + char (&ra)[5] = a; + const char (&rs)[5] = "test"; + const char (*pa)[5] = &a; + pa = &"test"; +} + +void FuncPtrConversions(int(*pfn)(int), void* p) { + p = (void*)pfn; + pfn = (int(*)(int))p; +} + +void VarArgUsage(int x, ...) { + __builtin_va_list args; + + __builtin_va_start(args, x); + __builtin_va_list args2; + __builtin_va_start(args2, args); + double d = __builtin_va_arg(args, double); + float f = __builtin_va_arg(args, float); + __builtin_va_end(args); + __builtin_va_end(args2); +} + +void CastToVoid(int x) { + (void)x; +} + +void ConstantConditions(int x) { + bool a = true && true; + int b = (true) ? x : x; +} + +typedef unsigned long size_t; + +namespace std { + enum class align_val_t : size_t {}; +} + +void* operator new(size_t, float); +void* operator new[](size_t, float); +void* operator new(size_t, std::align_val_t, float); +void* operator new[](size_t, std::align_val_t, float); +void operator delete(void*, float); +void operator delete[](void*, float); +void operator delete(void*, std::align_val_t, float); +void operator delete[](void*, std::align_val_t, float); + +struct SizedDealloc { + char a[32]; + void* operator new(size_t); + void* operator new[](size_t); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); +}; + +struct alignas(128) Overaligned { + char a[256]; +}; + +struct DefaultCtorWithDefaultParam { + DefaultCtorWithDefaultParam(double d = 1.0); +}; + +void OperatorNew() { + new int; // No constructor + new(1.0f) int; // Placement new, no constructor + new int(); // Zero-init + new String(); // Constructor + new(1.0f) String("hello"); // Placement new, constructor with args + new Overaligned; // Aligned new + new(1.0f) Overaligned(); // Placement aligned new with zero-init +} + +void OperatorNewArray(int n) { + new int[10]; // Constant size + new int[n]; // No constructor + new(1.0f) int[n]; // Placement new, no constructor + new String[n]; // Constructor + new Overaligned[n]; // Aligned new + new(1.0f) Overaligned[10]; // Aligned placement new + new DefaultCtorWithDefaultParam[n]; + new int[n] { 0, 1, 2 }; +} + +int designatedInit() { + int a1[1000] = { [2] = 10002, [900] = 10900 }; + return a1[900]; +} + +#if 0 +void OperatorDelete() { + delete static_cast(nullptr); // No destructor + delete static_cast(nullptr); // Non-virtual destructor, with size. + delete static_cast(nullptr); // No destructor, with size. + delete static_cast(nullptr); // No destructor, with size and alignment. + delete static_cast(nullptr); // Virtual destructor +} + +void OperatorDeleteArray() { + delete[] static_cast(nullptr); // No destructor + delete[] static_cast(nullptr); // Non-virtual destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size. + delete[] static_cast(nullptr); // No destructor, with size and alignment. + delete[] static_cast(nullptr); // Virtual destructor +} +#endif + +// semmle-extractor-options: -std=c++17 diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c index 22992df95ea7..ee5c611b43fe 100644 --- a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals.c @@ -1,102 +1,102 @@ -struct someStruct { - int i; - int j; -}; - -struct someOtherStruct { - int a; - int b; -}; - -union someUnion { - int n; - double d; -}; - -void f(int x, int y) { - struct someStruct sInit1 = { - .i = x + x, - .j = y - y, - }; - - struct someStruct sInit2 = { x + x, y - y }; - - struct someStruct ss[] = {{x + x, y - y}, {x * x, y / y}}; - - struct someStruct sInit3 = { x }; - - struct someStruct sInit4 = { .j = y }; - - int aInit1[2] = { x, y }; - - int aInit2[2] = { x }; - - int aInit3[2] = { [1] = y }; - - union someUnion uInit1 = { x }; - union someUnion uInit2 = { .n = x }; - union someUnion uInit3 = { .d = 5.0 }; -} - -struct complexStruct { - struct someStruct sss[3]; - int as[3]; - struct someOtherStruct soss[3]; - int z; -}; - -void g(int x, int y) { - // Nested aggregate designated initializers - struct complexStruct complexInit1 = { - .as = { - [2] = x, - [0] = y, - x+y // as[1] - }, - .z = 42, - .soss = { - [1] = { - .a = x+y, - .b = x-y - }, - [0] = { - .b = x*y, - .a = x/y - } - // soss[2] is value initializaed - } - // sss is value initialized - }; - - // Nested aggregate non-designated initializers - struct complexStruct complexInit2 = { - { // sss - { // sss[0] - x, // sss[0].i - y // sss[0].j - }, - { // sss[1] - x+1, // sss[1].i - // sss[1].j is value initialized - } - // ss[2] is value initialized - }, - { // as - 99, // as[0] - x*y // as[1] - // as[2] is value initialized - }, - { // soss - { // soss[0] - 123, // soss[0].a - y+1 // soss[0].b - }, - { // soss[1] - x, // soss[1].a - // soss[1].b is value initialized - } - // soss[2] is value initialized - } - // z is value initialized - }; -} +struct someStruct { + int i; + int j; +}; + +struct someOtherStruct { + int a; + int b; +}; + +union someUnion { + int n; + double d; +}; + +void f(int x, int y) { + struct someStruct sInit1 = { + .i = x + x, + .j = y - y, + }; + + struct someStruct sInit2 = { x + x, y - y }; + + struct someStruct ss[] = {{x + x, y - y}, {x * x, y / y}}; + + struct someStruct sInit3 = { x }; + + struct someStruct sInit4 = { .j = y }; + + int aInit1[2] = { x, y }; + + int aInit2[2] = { x }; + + int aInit3[2] = { [1] = y }; + + union someUnion uInit1 = { x }; + union someUnion uInit2 = { .n = x }; + union someUnion uInit3 = { .d = 5.0 }; +} + +struct complexStruct { + struct someStruct sss[3]; + int as[3]; + struct someOtherStruct soss[3]; + int z; +}; + +void g(int x, int y) { + // Nested aggregate designated initializers + struct complexStruct complexInit1 = { + .as = { + [2] = x, + [0] = y, + x+y // as[1] + }, + .z = 42, + .soss = { + [1] = { + .a = x+y, + .b = x-y + }, + [0] = { + .b = x*y, + .a = x/y + } + // soss[2] is value initializaed + } + // sss is value initialized + }; + + // Nested aggregate non-designated initializers + struct complexStruct complexInit2 = { + { // sss + { // sss[0] + x, // sss[0].i + y // sss[0].j + }, + { // sss[1] + x+1, // sss[1].i + // sss[1].j is value initialized + } + // ss[2] is value initialized + }, + { // as + 99, // as[0] + x*y // as[1] + // as[2] is value initialized + }, + { // soss + { // soss[0] + 123, // soss[0].a + y+1 // soss[0].b + }, + { // soss[1] + x, // soss[1].a + // soss[1].b is value initialized + } + // soss[2] is value initialized + } + // z is value initialized + }; +} diff --git a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp index ba1acfbe0ffb..8b335fe3caed 100644 --- a/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp +++ b/cpp/ql/test/library-tests/literals/aggregate_literals/aggregate_literals_cpp.cpp @@ -1,28 +1,28 @@ -struct StructWithBitfields { - void Method(); - - unsigned int a : 12; - - static int s; // Static member variables aren't initialized - - unsigned int b : 5; - unsigned int : 15; // Unnamed bitfields aren't initialized - unsigned int c : 7; -}; - -union UnionWithMethods { - void Method(); - - static int s; - - double d; - int x; -}; - -void Init(int x, int y, int z) { - StructWithBitfields s1 = { x, y, z }; - StructWithBitfields s2 = { x, y }; // s2.c is value initialized - StructWithBitfields s3 = {}; // s3 is value initialized - UnionWithMethods u1 = { x }; - UnionWithMethods u2 = {}; -} +struct StructWithBitfields { + void Method(); + + unsigned int a : 12; + + static int s; // Static member variables aren't initialized + + unsigned int b : 5; + unsigned int : 15; // Unnamed bitfields aren't initialized + unsigned int c : 7; +}; + +union UnionWithMethods { + void Method(); + + static int s; + + double d; + int x; +}; + +void Init(int x, int y, int z) { + StructWithBitfields s1 = { x, y, z }; + StructWithBitfields s2 = { x, y }; // s2.c is value initialized + StructWithBitfields s3 = {}; // s3 is value initialized + UnionWithMethods u1 = { x }; + UnionWithMethods u2 = {}; +} diff --git a/cpp/ql/test/library-tests/loops/loops.cpp b/cpp/ql/test/library-tests/loops/loops.cpp index e87c918225cd..44450e745b03 100644 --- a/cpp/ql/test/library-tests/loops/loops.cpp +++ b/cpp/ql/test/library-tests/loops/loops.cpp @@ -1,36 +1,36 @@ -void update(bool b); -void init(int &i); -bool cond(int i); -void update(int &i); - -void test_while() -{ - bool b; - - b = true; - while (b) update(b); - - do - { - update(b); - } while (b); -} - -void test_for() -{ - int i, j; - - for (i = 0; i < 10; i++) { - } - - for (j = 10; j >= 0; ) j--; - - for (i = 0, j = 0; i < 10; i++, j++) { - } - - for (init(i); cond(i); update(i)) { - } - - for ((i = 100); (i >= 0); (i--)) { - } -} +void update(bool b); +void init(int &i); +bool cond(int i); +void update(int &i); + +void test_while() +{ + bool b; + + b = true; + while (b) update(b); + + do + { + update(b); + } while (b); +} + +void test_for() +{ + int i, j; + + for (i = 0; i < 10; i++) { + } + + for (j = 10; j >= 0; ) j--; + + for (i = 0, j = 0; i < 10; i++, j++) { + } + + for (init(i); cond(i); update(i)) { + } + + for ((i = 100); (i >= 0); (i--)) { + } +} diff --git a/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp index f75c9883dbb7..e9a28fe1b1f8 100644 --- a/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp +++ b/cpp/ql/test/library-tests/namespaces/namespaces/namespaces.cpp @@ -1,44 +1,44 @@ -namespace A { - -} - -namespace B { - -} - -namespace C { - - namespace D { - - inline int f() { return 0; } - - class E { - - void g(int p) { - int a; - { - int b; - } - } - - }; - - } - -} - -namespace B { - - int x = C::D::f(); - -} - -namespace std { - -} - -int globalInt; - -void globalIntUser(void) { - extern int globalInt; -} +namespace A { + +} + +namespace B { + +} + +namespace C { + + namespace D { + + inline int f() { return 0; } + + class E { + + void g(int p) { + int a; + { + int b; + } + } + + }; + + } + +} + +namespace B { + + int x = C::D::f(); + +} + +namespace std { + +} + +int globalInt; + +void globalIntUser(void) { + extern int globalInt; +} diff --git a/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp b/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp index 6e7d7204e73f..8f45748a1d73 100644 --- a/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp +++ b/cpp/ql/test/library-tests/namespaces/same_name/same_name.cpp @@ -1,16 +1,16 @@ - -const int c = 1; - -namespace namespace_a -{ - const int c = 1; -} - -namespace namespace_b -{ - //const int c = 1; - // - // this example is causing a DBCheck failure along the lines of: - // - // [INVALID_KEY] Relation namespacembrs((@namespace parentid, unique @namespacembr memberid)): Value 132 of key field memberid occurs in several tuples. Two such tuples are: (134,132) and (144,132) -} + +const int c = 1; + +namespace namespace_a +{ + const int c = 1; +} + +namespace namespace_b +{ + //const int c = 1; + // + // this example is causing a DBCheck failure along the lines of: + // + // [INVALID_KEY] Relation namespacembrs((@namespace parentid, unique @namespacembr memberid)): Value 132 of key field memberid occurs in several tuples. Two such tuples are: (134,132) and (144,132) +} diff --git a/cpp/ql/test/library-tests/operators/operators.cpp b/cpp/ql/test/library-tests/operators/operators.cpp index 790342977257..164d2c19e848 100644 --- a/cpp/ql/test/library-tests/operators/operators.cpp +++ b/cpp/ql/test/library-tests/operators/operators.cpp @@ -1,27 +1,27 @@ -class Name { - const char* s; -}; - -class Table { - Name* p; - int sz; -public: - // See JIRA 521. Including a non-default copy constructor for now. - Table(const Table& other) { } - Table(int s=15) { p = new Name[sz=s]; } // constructor - ~Table() { delete[] p; } - Name* lookup (const char*); - bool insert(Name*); -}; - -void foo() { - Table t1; // call to user-defined constructor - Table t2(t1); // call to default copy constructor - t2.insert(0); -} - -// This used to have TRAP import errors -struct A { A() {} A(A&) {} } a; -A operator+(int, A) { return a; } -int f_A() { 0 + a; } - +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + // See JIRA 521. Including a non-default copy constructor for now. + Table(const Table& other) { } + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +void foo() { + Table t1; // call to user-defined constructor + Table t2(t1); // call to default copy constructor + t2.insert(0); +} + +// This used to have TRAP import errors +struct A { A() {} A(A&) {} } a; +A operator+(int, A) { return a; } +int f_A() { 0 + a; } + diff --git a/cpp/ql/test/library-tests/padding/size_asserts.h b/cpp/ql/test/library-tests/padding/size_asserts.h index 674257cba41a..1b68ed8472df 100644 --- a/cpp/ql/test/library-tests/padding/size_asserts.h +++ b/cpp/ql/test/library-tests/padding/size_asserts.h @@ -1,46 +1,46 @@ -// Static assert macro -#define CASSERT(condition) typedef char cassertTypedef[((condition) != 0) ? 1 : -1] - -#if defined(_MSC_VER) -#define CASSERT_MSVC(condition) CASSERT(condition) -#define CASSERT_GCC(condition) -#if defined(_WIN64) -#define TARGET_BIT_SIZE 64 -#else -#define TARGET_BIT_SIZE 32 -#endif -#elif defined(__GNUC__) -#define CASSERT_MSVC(condition) -#define CASSERT_GCC(condition) CASSERT(condition) -#if defined(__x86_64) -#define TARGET_BIT_SIZE 64 -#else -#define TARGET_BIT_SIZE 32 -#endif -#else -CASSERT(0); -#endif - -#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 32) -#define CASSERT_MSVC32(condition) CASSERT(condition) -#else -#define CASSERT_MSVC32(condition) -#endif - -#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 64) -#define CASSERT_MSVC64(condition) CASSERT(condition) -#else -#define CASSERT_MSVC64(condition) -#endif - -#if defined(__GNUC__) && (TARGET_BIT_SIZE == 32) -#define CASSERT_GCC32(condition) CASSERT(condition) -#else -#define CASSERT_GCC32(condition) -#endif - -#if defined(__GNUC__) && (TARGET_BIT_SIZE == 64) -#define CASSERT_GCC64(condition) CASSERT(condition) -#else -#define CASSERT_GCC64(condition) -#endif +// Static assert macro +#define CASSERT(condition) typedef char cassertTypedef[((condition) != 0) ? 1 : -1] + +#if defined(_MSC_VER) +#define CASSERT_MSVC(condition) CASSERT(condition) +#define CASSERT_GCC(condition) +#if defined(_WIN64) +#define TARGET_BIT_SIZE 64 +#else +#define TARGET_BIT_SIZE 32 +#endif +#elif defined(__GNUC__) +#define CASSERT_MSVC(condition) +#define CASSERT_GCC(condition) CASSERT(condition) +#if defined(__x86_64) +#define TARGET_BIT_SIZE 64 +#else +#define TARGET_BIT_SIZE 32 +#endif +#else +CASSERT(0); +#endif + +#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 32) +#define CASSERT_MSVC32(condition) CASSERT(condition) +#else +#define CASSERT_MSVC32(condition) +#endif + +#if defined(_MSC_VER) && (TARGET_BIT_SIZE == 64) +#define CASSERT_MSVC64(condition) CASSERT(condition) +#else +#define CASSERT_MSVC64(condition) +#endif + +#if defined(__GNUC__) && (TARGET_BIT_SIZE == 32) +#define CASSERT_GCC32(condition) CASSERT(condition) +#else +#define CASSERT_GCC32(condition) +#endif + +#if defined(__GNUC__) && (TARGET_BIT_SIZE == 64) +#define CASSERT_GCC64(condition) CASSERT(condition) +#else +#define CASSERT_GCC64(condition) +#endif diff --git a/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp b/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp index 1c85d65409bf..c0965c331725 100644 --- a/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp +++ b/cpp/ql/test/library-tests/parameters/parameters/parameters.cpp @@ -1,28 +1,28 @@ -typedef int (*CallbackFn)(int a, int b); - -int Callback_1(int a, int b) -{ - return a + b; -} - -int Callback_2(int a, int b) -{ - return a; -} - -int Callback_3(int, int b) -{ - return b; -} - -void Dispatch(int n, int a, int b, int c, int) -{ - CallbackFn pFn; - switch(n) - { - case 0: pFn = &Callback_1; break; - case 1: pFn = &Callback_2; break; - default: pFn = &Callback_3; break; - } - (*pFn)(a,b); -} +typedef int (*CallbackFn)(int a, int b); + +int Callback_1(int a, int b) +{ + return a + b; +} + +int Callback_2(int a, int b) +{ + return a; +} + +int Callback_3(int, int b) +{ + return b; +} + +void Dispatch(int n, int a, int b, int c, int) +{ + CallbackFn pFn; + switch(n) + { + case 0: pFn = &Callback_1; break; + case 1: pFn = &Callback_2; break; + default: pFn = &Callback_3; break; + } + (*pFn)(a,b); +} diff --git a/cpp/ql/test/library-tests/preprocessor/include/guarded.h b/cpp/ql/test/library-tests/preprocessor/include/guarded.h index 96fb298d5c96..fb7c9149e9ff 100644 --- a/cpp/ql/test/library-tests/preprocessor/include/guarded.h +++ b/cpp/ql/test/library-tests/preprocessor/include/guarded.h @@ -1,8 +1,8 @@ -// guarded.h - -#ifndef GUARDED_H -#define GUARDED_H - - // ... content ... - +// guarded.h + +#ifndef GUARDED_H +#define GUARDED_H + + // ... content ... + #endif // GUARDED_H \ No newline at end of file diff --git a/cpp/ql/test/library-tests/preprocessor/include/unguarded.h b/cpp/ql/test/library-tests/preprocessor/include/unguarded.h index 14f174c8729f..bf1b9844e68a 100644 --- a/cpp/ql/test/library-tests/preprocessor/include/unguarded.h +++ b/cpp/ql/test/library-tests/preprocessor/include/unguarded.h @@ -1,3 +1,3 @@ -// unguarded.h - -// ... content ... +// unguarded.h + +// ... content ... diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/header.h b/cpp/ql/test/library-tests/preprocessor/linedirective/header.h index 51fb5f568d65..6865877d9504 100644 --- a/cpp/ql/test/library-tests/preprocessor/linedirective/header.h +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/header.h @@ -1,3 +1,3 @@ -// header.h - -// ... content ... +// header.h + +// ... content ... diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/line.h b/cpp/ql/test/library-tests/preprocessor/linedirective/line.h index 5ec8cc56e1f0..1b4fc1a6aeee 100644 --- a/cpp/ql/test/library-tests/preprocessor/linedirective/line.h +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/line.h @@ -1,14 +1,14 @@ -// line.h - -#line 100 "line.h" - -#include "header.h" - -void myLineFunction(); - -#define MYLINEDEFINE (1) - -struct myLineStruct -{ - int v; -}; +// line.h + +#line 100 "line.h" + +#include "header.h" + +void myLineFunction(); + +#define MYLINEDEFINE (1) + +struct myLineStruct +{ + int v; +}; diff --git a/cpp/ql/test/library-tests/preprocessor/linedirective/system.h b/cpp/ql/test/library-tests/preprocessor/linedirective/system.h index eec178192a5f..407507af89a1 100644 --- a/cpp/ql/test/library-tests/preprocessor/linedirective/system.h +++ b/cpp/ql/test/library-tests/preprocessor/linedirective/system.h @@ -1,14 +1,14 @@ -// system.h - -#pragma GCC system_header - -#include "header.h" - -void mySystemFunction(); - -#define MYSYSTEMDEFINE (2) - -struct mySystemStruct -{ - int v; -}; +// system.h + +#pragma GCC system_header + +#include "header.h" + +void mySystemFunction(); + +#define MYSYSTEMDEFINE (2) + +struct mySystemStruct +{ + int v; +}; diff --git a/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp b/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp index 3e1353e1ff73..70dc0a195dd7 100644 --- a/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp +++ b/cpp/ql/test/library-tests/scopes/scopes/scopes.cpp @@ -1,104 +1,104 @@ - -class A { - -}; - - -class B { - -}; - -class C: A, B { - -}; - -class D: C { - - class E; - -}; - -class D::E { - - class F; - - class G { - - }; - -}; - -class D::E::F: D::E::G { - void doubleNestedFunction() {} -}; - -void f(int a, int b) { - bool c = a==b; - - switch (a) - { - default: - } -} - -void g(); - -struct S { - void af() { - - } - void ag(); -}; - -void g() { - S s; - s.ag(); -} - -class Name { - const char* s; -}; - -class Table { - Name* p; - int sz; -public: - Table(int s=15) { p = new Name[sz=s]; } // constructor - ~Table() { delete[] p; } - Name* lookup (const char*); - bool insert(Name*); -}; - -extern "C" int strlen(const char*); - -namespace One -{ - template - class H - { - T t; - }; - - H h_short; - H h_long; - - namespace Two - { - enum myEnum - { - myOne, - myTwo, - myThree - }; - }; - - class I - { - enum myEnum2 - { - Alpha, - Beta, - Gamma - }; - }; + +class A { + +}; + + +class B { + +}; + +class C: A, B { + +}; + +class D: C { + + class E; + +}; + +class D::E { + + class F; + + class G { + + }; + +}; + +class D::E::F: D::E::G { + void doubleNestedFunction() {} +}; + +void f(int a, int b) { + bool c = a==b; + + switch (a) + { + default: + } +} + +void g(); + +struct S { + void af() { + + } + void ag(); +}; + +void g() { + S s; + s.ag(); +} + +class Name { + const char* s; +}; + +class Table { + Name* p; + int sz; +public: + Table(int s=15) { p = new Name[sz=s]; } // constructor + ~Table() { delete[] p; } + Name* lookup (const char*); + bool insert(Name*); +}; + +extern "C" int strlen(const char*); + +namespace One +{ + template + class H + { + T t; + }; + + H h_short; + H h_long; + + namespace Two + { + enum myEnum + { + myOne, + myTwo, + myThree + }; + }; + + class I + { + enum myEnum2 + { + Alpha, + Beta, + Gamma + }; + }; }; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp index d28e8513e841..30b0f5cf6fc9 100644 --- a/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp +++ b/cpp/ql/test/library-tests/sideEffects/exprs/exprs.cpp @@ -1,40 +1,40 @@ - -class MyIterator - { -public: - MyIterator &operator++() - { - return (*this); - } - - MyIterator &operator--() - { - v--; - return (*this); - } - -private: - int v; - }; - -void f2() { - MyIterator mi; - - ++mi; - --mi; -} - -template void myTemplateFunction() { - static MyIterator mi; - static int i; - static T t; - - ++mi; // pure - ++i; // impure - ++t; // varies -} - -void f3() { - myTemplateFunction(); - myTemplateFunction(); -} + +class MyIterator + { +public: + MyIterator &operator++() + { + return (*this); + } + + MyIterator &operator--() + { + v--; + return (*this); + } + +private: + int v; + }; + +void f2() { + MyIterator mi; + + ++mi; + --mi; +} + +template void myTemplateFunction() { + static MyIterator mi; + static int i; + static T t; + + ++mi; // pure + ++i; // impure + ++t; // varies +} + +void f3() { + myTemplateFunction(); + myTemplateFunction(); +} diff --git a/cpp/ql/test/library-tests/specifiers/specifiers.cpp b/cpp/ql/test/library-tests/specifiers/specifiers.cpp index db40595b09cb..c103e4198c73 100644 --- a/cpp/ql/test/library-tests/specifiers/specifiers.cpp +++ b/cpp/ql/test/library-tests/specifiers/specifiers.cpp @@ -1,22 +1,22 @@ -extern int i; -extern int i; -extern int i; - -const int c = 7; -const double pi = 3.1415926535897932385; - -unsigned a; - -unsigned int b; -int b2; - - int * const int_pointer1 = &b2; -const int * int_pointer2 = &c; // Note: int_pointer2 is not const itself -const int * const int_pointer3 = &c; - -typedef char* Pchar; - -const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; - -int* p, q; -int v[10], *pv; +extern int i; +extern int i; +extern int i; + +const int c = 7; +const double pi = 3.1415926535897932385; + +unsigned a; + +unsigned int b; +int b2; + + int * const int_pointer1 = &b2; +const int * int_pointer2 = &c; // Note: int_pointer2 is not const itself +const int * const int_pointer3 = &c; + +typedef char* Pchar; + +const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; + +int* p, q; +int v[10], *pv; diff --git a/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp b/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp index 283baabd2ac2..7d98961f04b2 100644 --- a/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp +++ b/cpp/ql/test/library-tests/string_analysis/string_analysis.cpp @@ -1,44 +1,44 @@ -int main(int argc, char *argv[]) -{ - char *str1, *str2, *str3, *result; - int cond1, cond2, cond3, cond4; - - str1 = "1"; - str2 = "22"; - str3 = "333"; - - result = str1; // max length 1 - if (cond1) - { - result = (cond2 ? str2 : str3); // max length 3 - } - result = (cond3 ? str1 : result); // max length 3 - result = (cond4 ? str1 : argv[0]); // max unknown - - return 0; -} - -namespace std -{ - class string - { - public: - string(char *_str) : str(_str) {}; - ~string() {}; - - string &operator=(string &other) { - str = other.str; - }; - - private: - char *str; - }; -} - -void more_cases() -{ - wchar_t *wstr1 = L"4444"; - wchar_t *wstr2 = wstr1; - std::string str1 = "666666"; - std::string str2 = str1; -} +int main(int argc, char *argv[]) +{ + char *str1, *str2, *str3, *result; + int cond1, cond2, cond3, cond4; + + str1 = "1"; + str2 = "22"; + str3 = "333"; + + result = str1; // max length 1 + if (cond1) + { + result = (cond2 ? str2 : str3); // max length 3 + } + result = (cond3 ? str1 : result); // max length 3 + result = (cond4 ? str1 : argv[0]); // max unknown + + return 0; +} + +namespace std +{ + class string + { + public: + string(char *_str) : str(_str) {}; + ~string() {}; + + string &operator=(string &other) { + str = other.str; + }; + + private: + char *str; + }; +} + +void more_cases() +{ + wchar_t *wstr1 = L"4444"; + wchar_t *wstr2 = wstr1; + std::string str1 = "666666"; + std::string str2 = str1; +} diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/a.c b/cpp/ql/test/library-tests/structs/compatible_variables/a.c index bac709efb954..6f6b1800d8b6 100644 --- a/cpp/ql/test/library-tests/structs/compatible_variables/a.c +++ b/cpp/ql/test/library-tests/structs/compatible_variables/a.c @@ -1,14 +1,14 @@ -#include "h.h" - -// Provide a complete definition of Foo. -struct Foo { - int foo_x; -}; - -// This definition is incompatible with the one in b.c, so... -struct Bar { - int bar_y; -}; - -// ...we'd expect this declaration to create a separate variable in the db -extern struct Bar bar; +#include "h.h" + +// Provide a complete definition of Foo. +struct Foo { + int foo_x; +}; + +// This definition is incompatible with the one in b.c, so... +struct Bar { + int bar_y; +}; + +// ...we'd expect this declaration to create a separate variable in the db +extern struct Bar bar; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/b.c b/cpp/ql/test/library-tests/structs/compatible_variables/b.c index 5037af9500dc..a7dd16e2d38d 100644 --- a/cpp/ql/test/library-tests/structs/compatible_variables/b.c +++ b/cpp/ql/test/library-tests/structs/compatible_variables/b.c @@ -1,17 +1,17 @@ -// The extractor will not see a complete definition of Foo for this file. - -#include "h.h" - -// We want to check that these two variables don't get duplicated in the -// database. -void (*some_func_ptr)(struct Foo *foo); -struct Foo* foo_ptr1; -FooPtr foo_ptr2; - -// This definition is incompatible with the one in a.c, so... -struct Bar { - unsigned long bar_x; -}; - -// ...we'd expect this declaration to create a separate variable in the db -extern struct Bar bar; +// The extractor will not see a complete definition of Foo for this file. + +#include "h.h" + +// We want to check that these two variables don't get duplicated in the +// database. +void (*some_func_ptr)(struct Foo *foo); +struct Foo* foo_ptr1; +FooPtr foo_ptr2; + +// This definition is incompatible with the one in a.c, so... +struct Bar { + unsigned long bar_x; +}; + +// ...we'd expect this declaration to create a separate variable in the db +extern struct Bar bar; diff --git a/cpp/ql/test/library-tests/structs/compatible_variables/h.h b/cpp/ql/test/library-tests/structs/compatible_variables/h.h index 95cf6d428608..b94a97f9a4b8 100644 --- a/cpp/ql/test/library-tests/structs/compatible_variables/h.h +++ b/cpp/ql/test/library-tests/structs/compatible_variables/h.h @@ -1,10 +1,10 @@ -// Provide an incomplete definition of Foo. -struct Foo; -typedef struct Foo* FooPtr; - -// When this file is included from a.c, the extractor will see a complete -// definition of Foo, but not when it's included b.c. We want to check that we -// don't see these variables duplicated in the database because of it. -extern void (*some_func_ptr)(struct Foo *foo); -extern struct Foo* foo_ptr1; -extern FooPtr foo_ptr2; +// Provide an incomplete definition of Foo. +struct Foo; +typedef struct Foo* FooPtr; + +// When this file is included from a.c, the extractor will see a complete +// definition of Foo, but not when it's included b.c. We want to check that we +// don't see these variables duplicated in the database because of it. +extern void (*some_func_ptr)(struct Foo *foo); +extern struct Foo* foo_ptr1; +extern FooPtr foo_ptr2; diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/a.h b/cpp/ql/test/library-tests/structs/incomplete_definition/a.h index d09cac85aa56..a051126f2718 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/a.h +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/a.h @@ -1,6 +1,6 @@ -// Incomplete definition of Foo -struct Foo; - -struct Bar { - Foo *cheese; -}; +// Incomplete definition of Foo +struct Foo; + +struct Bar { + Foo *cheese; +}; diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp index ab5eb9474553..e0e0338c8310 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/x.cpp @@ -1,31 +1,31 @@ -#include "a.h" - -Bar *bar_x; - -namespace unrelated { - struct Foo { - short val; - }; -} - -struct ContainsAnotherFoo { - class Foo { - long val; - }; -}; - -// The type of `foo_x` should not refer to any of the above classes, none of -// which are named `Foo` in the global scope. -Foo *foo_x; - -template -class Template { - T templateField; -}; - -Template *template_foo; - -// Instantiation of the template with unrelated classes named `Foo` should not -// get mixed up with the instantiation above. -template class Template; -template class Template; +#include "a.h" + +Bar *bar_x; + +namespace unrelated { + struct Foo { + short val; + }; +} + +struct ContainsAnotherFoo { + class Foo { + long val; + }; +}; + +// The type of `foo_x` should not refer to any of the above classes, none of +// which are named `Foo` in the global scope. +Foo *foo_x; + +template +class Template { + T templateField; +}; + +Template *template_foo; + +// Instantiation of the template with unrelated classes named `Foo` should not +// get mixed up with the instantiation above. +template class Template; +template class Template; diff --git a/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp b/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp index 6059af317665..ae8de1d55fa8 100644 --- a/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp +++ b/cpp/ql/test/library-tests/structs/incomplete_definition/y.cpp @@ -1,8 +1,8 @@ -#include "a.h" - -// Completes definition of Foo -struct Foo { - int val; -}; - -Bar bar_y; +#include "a.h" + +// Completes definition of Foo +struct Foo { + int val; +}; + +Bar bar_y; diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/a.c b/cpp/ql/test/library-tests/structs/mutual_recursion/a.c index 6fd3d62ba247..6af982609adb 100644 --- a/cpp/ql/test/library-tests/structs/mutual_recursion/a.c +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/a.c @@ -1,61 +1,61 @@ -struct Node { - struct Node *next; -}; - -struct CompatibleB; -struct CompatibleC; - -struct CompatibleA { - struct CompatibleB *b; -}; - -struct CompatibleB { - struct CompatibleC *c; -}; - -// The 2 definitions of CompatibleC use different but compatible types for x -struct CompatibleC { - struct CompatibleA *a; - int x; -}; - -// The initial handling of recursion didn't catch this case - if you start from -// D, you'll never revisit it, but you will revisit A/B/C. -struct CompatibleD { - struct CompatibleA *a; -}; - -// Ideally, we'd detect that the definitions of Incompatible{A,B} are incompatible, but since their -// fields are pointers, we can't deeply inspect them (it would be possible to have pointers to -// incomplete types that we *can't* deeply inspect). -struct IncompatibleB; -struct IncompatibleC; - -struct IncompatibleA { - struct IncompatibleB *b; -}; - -struct IncompatibleB { - struct IncompatibleC *c; -}; - -// The 2 definitions of IncompatibleC use different and incompatible types for x -struct IncompatibleC { - struct IncompatibleA *a; - short x; -}; - -struct IncompatibleD { - struct IncompatibleA *a; -}; - -// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are -// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can -// deeply inspect NonRecursiveA when it's used from NonRecursiveB. -struct NonRecursiveA { - int val; -}; - -struct NonRecursiveB { - struct NonRecursiveA a; -}; +struct Node { + struct Node *next; +}; + +struct CompatibleB; +struct CompatibleC; + +struct CompatibleA { + struct CompatibleB *b; +}; + +struct CompatibleB { + struct CompatibleC *c; +}; + +// The 2 definitions of CompatibleC use different but compatible types for x +struct CompatibleC { + struct CompatibleA *a; + int x; +}; + +// The initial handling of recursion didn't catch this case - if you start from +// D, you'll never revisit it, but you will revisit A/B/C. +struct CompatibleD { + struct CompatibleA *a; +}; + +// Ideally, we'd detect that the definitions of Incompatible{A,B} are incompatible, but since their +// fields are pointers, we can't deeply inspect them (it would be possible to have pointers to +// incomplete types that we *can't* deeply inspect). +struct IncompatibleB; +struct IncompatibleC; + +struct IncompatibleA { + struct IncompatibleB *b; +}; + +struct IncompatibleB { + struct IncompatibleC *c; +}; + +// The 2 definitions of IncompatibleC use different and incompatible types for x +struct IncompatibleC { + struct IncompatibleA *a; + short x; +}; + +struct IncompatibleD { + struct IncompatibleA *a; +}; + +// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are +// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can +// deeply inspect NonRecursiveA when it's used from NonRecursiveB. +struct NonRecursiveA { + int val; +}; + +struct NonRecursiveB { + struct NonRecursiveA a; +}; diff --git a/cpp/ql/test/library-tests/structs/mutual_recursion/b.c b/cpp/ql/test/library-tests/structs/mutual_recursion/b.c index 234e1f84e49d..1b4d486059a6 100644 --- a/cpp/ql/test/library-tests/structs/mutual_recursion/b.c +++ b/cpp/ql/test/library-tests/structs/mutual_recursion/b.c @@ -1,57 +1,57 @@ -struct Node { - struct Node *next; -}; - -struct CompatibleB; -struct CompatibleC; - -struct CompatibleA { - struct CompatibleB *b; -}; - -struct CompatibleB { - struct CompatibleC *c; -}; - -// The 2 definitions of CompatibleC use different but compatible types for x -typedef int AnInt; -struct CompatibleC { - struct CompatibleA *a; - AnInt x; -}; - -struct CompatibleD { - struct CompatibleA *a; -}; - -struct IncompatibleB; -struct IncompatibleC; - -struct IncompatibleA { - struct IncompatibleB *b; -}; - -struct IncompatibleB { - struct IncompatibleC *c; -}; - -// The 2 definitions of IncompatibleC use different and incompatible types for x -struct IncompatibleC { - struct IncompatibleA *a; - int x; -}; - -struct IncompatibleD { - struct IncompatibleA *a; -}; - -// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are -// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can -// deeply inspect NonRecursiveA when it's used from NonRecursiveB. -struct NonRecursiveA { - short val; -}; - -struct NonRecursiveB { - struct NonRecursiveA a; -}; +struct Node { + struct Node *next; +}; + +struct CompatibleB; +struct CompatibleC; + +struct CompatibleA { + struct CompatibleB *b; +}; + +struct CompatibleB { + struct CompatibleC *c; +}; + +// The 2 definitions of CompatibleC use different but compatible types for x +typedef int AnInt; +struct CompatibleC { + struct CompatibleA *a; + AnInt x; +}; + +struct CompatibleD { + struct CompatibleA *a; +}; + +struct IncompatibleB; +struct IncompatibleC; + +struct IncompatibleA { + struct IncompatibleB *b; +}; + +struct IncompatibleB { + struct IncompatibleC *c; +}; + +// The 2 definitions of IncompatibleC use different and incompatible types for x +struct IncompatibleC { + struct IncompatibleA *a; + int x; +}; + +struct IncompatibleD { + struct IncompatibleA *a; +}; + +// We should be able to detect that the definitions of NonRecursiveA and NonRecursiveB are +// incompatible - unlike the above cases, there's no mutual recursion and no pointer type, so we can +// deeply inspect NonRecursiveA when it's used from NonRecursiveB. +struct NonRecursiveA { + short val; +}; + +struct NonRecursiveB { + struct NonRecursiveA a; +}; diff --git a/cpp/ql/test/library-tests/typedefs/typedefs.cpp b/cpp/ql/test/library-tests/typedefs/typedefs.cpp index 37043e5cf46f..6998b6c8ef7d 100644 --- a/cpp/ql/test/library-tests/typedefs/typedefs.cpp +++ b/cpp/ql/test/library-tests/typedefs/typedefs.cpp @@ -1,31 +1,31 @@ -namespace NS1 -{ - typedef int WIDTH; -} - -void f1() -{ - typedef int TYPE; - class D {}; -} - -class A -{ - -protected: - typedef NS1::WIDTH WIDTH; - typedef WIDTH WIDTH2; - -public: - typedef WIDTH WIDTH3; - -private: - typedef WIDTH WIDTH4; -}; - -class B: public A { - WIDTH i; - WIDTH2 i2; - WIDTH3 i3; - //WIDTH4 i4; --- inaccessible -}; +namespace NS1 +{ + typedef int WIDTH; +} + +void f1() +{ + typedef int TYPE; + class D {}; +} + +class A +{ + +protected: + typedef NS1::WIDTH WIDTH; + typedef WIDTH WIDTH2; + +public: + typedef WIDTH WIDTH3; + +private: + typedef WIDTH WIDTH4; +}; + +class B: public A { + WIDTH i; + WIDTH2 i2; + WIDTH3 i3; + //WIDTH4 i4; --- inaccessible +}; diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp b/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp index aab2fa52924e..cacb48b3b8cc 100644 --- a/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp +++ b/cpp/ql/test/library-tests/types/integral_types_ms/integral_types.cpp @@ -1,5 +1,5 @@ - -__int8 i8; -__int16 i16; -__int32 i32; -__int64 i64; + +__int8 i8; +__int16 i16; +__int32 i32; +__int64 i64; diff --git a/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c index 2881dc878539..65bca5fa9dac 100644 --- a/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c +++ b/cpp/ql/test/library-tests/types/pointertypes/pointertypes.c @@ -1,40 +1,40 @@ - -char c; -char *cp; - -const char cc; -char const cc2; -const char *ccp; -char const *ccp2; -char * const ccp3; -char const * const ccp4; - -volatile char vc; -char volatile vc2; -volatile char *vcp; -char volatile *vcp2; -char * volatile vcp3; -char volatile * volatile vcp4; - -char * restrict rcp; -char *__restrict__ rcp2; - -void *vp; - -const void *cvp; -void const *cvp2; -void * const cvp3; -void const * const cvp4; - -void *restrict rvp1; -void *__restrict__ rvp2; - -const struct s *csp; -struct s const *csp2; -struct s * const csp3; -struct s const * const csp4; - -struct s *restrict rsp1; -struct s *__restrict__ rsp2; - -char * const volatile restrict cvrcp; + +char c; +char *cp; + +const char cc; +char const cc2; +const char *ccp; +char const *ccp2; +char * const ccp3; +char const * const ccp4; + +volatile char vc; +char volatile vc2; +volatile char *vcp; +char volatile *vcp2; +char * volatile vcp3; +char volatile * volatile vcp4; + +char * restrict rcp; +char *__restrict__ rcp2; + +void *vp; + +const void *cvp; +void const *cvp2; +void * const cvp3; +void const * const cvp4; + +void *restrict rvp1; +void *__restrict__ rvp2; + +const struct s *csp; +struct s const *csp2; +struct s * const csp3; +struct s const * const csp4; + +struct s *restrict rsp1; +struct s *__restrict__ rsp2; + +char * const volatile restrict cvrcp; diff --git a/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp b/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp index 05564ae604be..8a277a1d7532 100644 --- a/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp +++ b/cpp/ql/test/library-tests/types/refersTo/refersTo.cpp @@ -1,20 +1,20 @@ - -class a -{ -}; - -typedef a &a_ref; -typedef a *a_ptr; -typedef a a_array[10]; - -template -class container -{ -public: - T t; -}; - -class strange : public container > -{ -public: -}; + +class a +{ +}; + +typedef a &a_ref; +typedef a *a_ptr; +typedef a a_array[10]; + +template +class container +{ +public: + T t; +}; + +class strange : public container > +{ +public: +}; diff --git a/cpp/ql/test/library-tests/types/scope/scope.cpp b/cpp/ql/test/library-tests/types/scope/scope.cpp index c9d0d962d628..2a7b6501d3f6 100644 --- a/cpp/ql/test/library-tests/types/scope/scope.cpp +++ b/cpp/ql/test/library-tests/types/scope/scope.cpp @@ -1,27 +1,27 @@ - -int a[100]; -typedef int my_array[200]; - -void g1(int b[300]); -void g1(int b[300]) { - int c[400]; - - typedef int my_array_2[500]; - my_array d; - my_array_2 e; - - class f - { - public: - void g2() - { - int g[600]; - } - - int h[700]; - }; - - int i = sizeof(int[800]); - auto j = new int[900]; - int *k = new int[1000]; -} + +int a[100]; +typedef int my_array[200]; + +void g1(int b[300]); +void g1(int b[300]) { + int c[400]; + + typedef int my_array_2[500]; + my_array d; + my_array_2 e; + + class f + { + public: + void g2() + { + int g[600]; + } + + int h[700]; + }; + + int i = sizeof(int[800]); + auto j = new int[900]; + int *k = new int[1000]; +} diff --git a/cpp/ql/test/library-tests/types/types/types.cpp b/cpp/ql/test/library-tests/types/types/types.cpp index ab811aea8298..903ede7c49ba 100644 --- a/cpp/ql/test/library-tests/types/types/types.cpp +++ b/cpp/ql/test/library-tests/types/types/types.cpp @@ -1,82 +1,82 @@ -extern int i; - -const int c = 7; -const double pi = 3.1415926535897932385; - -unsigned a; - -unsigned int b; - -typedef char* Pchar; - -const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; - -int* p, q; -int v1[10], *pv; - -int (*fp)(char *); // pointer to function - -float v2[3]; -char* v3[32]; - -int d2[10][20]; - -char v4[3] = { 'a', 'b', 0 }; - -int v5[8] = { 1, 2, 3, 4 }; - -char* p2 = "Plato"; -char p3[] = "Zeno"; - -char alpha[] = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -int av[] = { 1, 2 , 3, 4 }; -int* ap1 = av; -int* ap2 = &av[0]; -int* ap3 = &av[4]; - -int f(int* pi) -{ - void* pv = pi; - return 0; -} - -void f1(char* p) -{ - char s[] = "Gorm"; - const char* pc = s; - pc = p; - - char *const cp = s; - cp[3] = 'a'; - - const char *const cpc = s; - char const* pc2 = s; -} - -void f2() -{ - int i = 1; - int& r = i; - int x = r; - r = 2; - const double& cdr = 1; -} - -void increment(int& aa) { aa++; } - -__int128 i128; -signed __int128 i128s; -unsigned __int128 i128u; - -Pchar pchar; - -typedef unsigned long size_t; -typedef long ssize_t; -typedef long ptrdiff_t; - -size_t st; -ssize_t sst; -ptrdiff_t pdt; -wchar_t wct; +extern int i; + +const int c = 7; +const double pi = 3.1415926535897932385; + +unsigned a; + +unsigned int b; + +typedef char* Pchar; + +const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; + +int* p, q; +int v1[10], *pv; + +int (*fp)(char *); // pointer to function + +float v2[3]; +char* v3[32]; + +int d2[10][20]; + +char v4[3] = { 'a', 'b', 0 }; + +int v5[8] = { 1, 2, 3, 4 }; + +char* p2 = "Plato"; +char p3[] = "Zeno"; + +char alpha[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +int av[] = { 1, 2 , 3, 4 }; +int* ap1 = av; +int* ap2 = &av[0]; +int* ap3 = &av[4]; + +int f(int* pi) +{ + void* pv = pi; + return 0; +} + +void f1(char* p) +{ + char s[] = "Gorm"; + const char* pc = s; + pc = p; + + char *const cp = s; + cp[3] = 'a'; + + const char *const cpc = s; + char const* pc2 = s; +} + +void f2() +{ + int i = 1; + int& r = i; + int x = r; + r = 2; + const double& cdr = 1; +} + +void increment(int& aa) { aa++; } + +__int128 i128; +signed __int128 i128s; +unsigned __int128 i128u; + +Pchar pchar; + +typedef unsigned long size_t; +typedef long ssize_t; +typedef long ptrdiff_t; + +size_t st; +ssize_t sst; +ptrdiff_t pdt; +wchar_t wct; diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c b/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c index e1c0bcc4c4a2..f8c5cf9d2fae 100644 --- a/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/ms.c @@ -1,4 +1,4 @@ - -typedef unsigned short wchar_t; - -wchar_t *wstring; + +typedef unsigned short wchar_t; + +wchar_t *wstring; diff --git a/cpp/ql/test/library-tests/unions/unions.cpp b/cpp/ql/test/library-tests/unions/unions.cpp index 6122c4e379d4..a693e3bc9d80 100644 --- a/cpp/ql/test/library-tests/unions/unions.cpp +++ b/cpp/ql/test/library-tests/unions/unions.cpp @@ -1,23 +1,23 @@ - -enum Type { S, I }; - -struct Entry { - - char* name; - Type t; - char* s; - int i; - -}; - -union Value { - char* s; - int i; -}; - - -struct EntryWithMethod: Entry { - int getAsInt() { - return i; - } -}; + +enum Type { S, I }; + +struct Entry { + + char* name; + Type t; + char* s; + int i; + +}; + +union Value { + char* s; + int i; +}; + + +struct EntryWithMethod: Entry { + int getAsInt() { + return i; + } +}; diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp index 2003a299aabf..d18ffed10eaa 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp @@ -93,15 +93,15 @@ int regression_test00() { return x; } -struct Base { +struct Base { int b; }; -struct Derived : Base { +struct Derived : Base { int d; }; -int inheritanceConversions(Derived* pd) { +int inheritanceConversions(Derived* pd) { int x = pd->b; Base* pb = static_cast(pd); int y = pb->b; diff --git a/cpp/ql/test/library-tests/variables/variables/variables.cpp b/cpp/ql/test/library-tests/variables/variables/variables.cpp index 28aae6743741..cbca2b3b8221 100644 --- a/cpp/ql/test/library-tests/variables/variables/variables.cpp +++ b/cpp/ql/test/library-tests/variables/variables/variables.cpp @@ -1,53 +1,53 @@ -extern int i; -extern int i; -extern int i; - -const int c = 7; -const double pi = 3.1415926535897932385; - -unsigned a; - -unsigned int b; - -const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; - -int* p, q; -static int v1[10], *pv; - -int (*fp)(char *); // pointer to function - -float v2[3]; -char* v3[32]; - -int d2[10][20]; - -char v4[3] = { 'a', 'b', 0 }; - -int v5[8] = { 1, 2, 3, 4 }; - -char* p2 = "Plato"; -char p3[] = "Zeno"; - -char alpha[] = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -int av[] = { 1, 2 , 3, 4 }; -int* ap1 = av; -int* ap2 = &av[0]; -int* ap3 = &av[4]; - - -void f() { - char local[] = { 'a', 'b' }; - { - static int local; - } -} - -struct address { - char* name; - long int number; - char* street; - char* town; - static char* country; -}; +extern int i; +extern int i; +extern int i; + +const int c = 7; +const double pi = 3.1415926535897932385; + +unsigned a; + +unsigned int b; + +const char* kings[] = { "Antigonus", "Seleucus", "Ptolemy" }; + +int* p, q; +static int v1[10], *pv; + +int (*fp)(char *); // pointer to function + +float v2[3]; +char* v3[32]; + +int d2[10][20]; + +char v4[3] = { 'a', 'b', 0 }; + +int v5[8] = { 1, 2, 3, 4 }; + +char* p2 = "Plato"; +char p3[] = "Zeno"; + +char alpha[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +int av[] = { 1, 2 , 3, 4 }; +int* ap1 = av; +int* ap2 = &av[0]; +int* ap3 = &av[4]; + + +void f() { + char local[] = { 'a', 'b' }; + { + static int local; + } +} + +struct address { + char* name; + long int number; + char* street; + char* town; + static char* country; +}; diff --git a/cpp/ql/test/library-tests/virtual_functions/virtual_functions/virtual_functions.cpp b/cpp/ql/test/library-tests/virtual_functions/virtual_functions/virtual_functions.cpp index 2ecea3095387..61a4e4ec545f 100644 --- a/cpp/ql/test/library-tests/virtual_functions/virtual_functions/virtual_functions.cpp +++ b/cpp/ql/test/library-tests/virtual_functions/virtual_functions/virtual_functions.cpp @@ -1,48 +1,48 @@ -class A -{ -public: - virtual void withunusedpara(int * para1, int unusedpara) = 0; - virtual void withunusedpara(int * para1, int & para2) = 0; -}; - -class B1: public A -{ -public: - virtual void withunusedpara(int * para1, int unusedpara) - { - *para1 = 1U; - } - virtual void withunusedpara(int * para1, int & para2) - { - *para1 = 1U; - } -}; - -class B2: public A -{ -public: - virtual void withunusedpara(int * para1, int unusedpara) - { - *para1 = 1U; - } - virtual void withunusedpara(int * para1, int & para2) - { - para2 = 0; - } -}; - -struct X1 { virtual void f() {} }; -struct X2 : X1 {}; -struct X3 : X2 { void f() {} }; -struct X4 : X2 { void f() {} }; -struct X5 : X3, X4 { void f() {} }; -struct X6 : X5 {}; -struct X7 : X6 { void f() {} }; - -struct Y1 { virtual void f() {} }; -struct Y2 : Y1 {}; -struct Y3 : virtual Y2 { void f() {} }; -struct Y4 : virtual Y2 { void f() {} }; -struct Y5 : Y3, Y4 { void f() {} }; -struct Y6 : Y5 {}; -struct Y7 : Y6 { void f() {} }; +class A +{ +public: + virtual void withunusedpara(int * para1, int unusedpara) = 0; + virtual void withunusedpara(int * para1, int & para2) = 0; +}; + +class B1: public A +{ +public: + virtual void withunusedpara(int * para1, int unusedpara) + { + *para1 = 1U; + } + virtual void withunusedpara(int * para1, int & para2) + { + *para1 = 1U; + } +}; + +class B2: public A +{ +public: + virtual void withunusedpara(int * para1, int unusedpara) + { + *para1 = 1U; + } + virtual void withunusedpara(int * para1, int & para2) + { + para2 = 0; + } +}; + +struct X1 { virtual void f() {} }; +struct X2 : X1 {}; +struct X3 : X2 { void f() {} }; +struct X4 : X2 { void f() {} }; +struct X5 : X3, X4 { void f() {} }; +struct X6 : X5 {}; +struct X7 : X6 { void f() {} }; + +struct Y1 { virtual void f() {} }; +struct Y2 : Y1 {}; +struct Y3 : virtual Y2 { void f() {} }; +struct Y4 : virtual Y2 { void f() {} }; +struct Y5 : Y3, Y4 { void f() {} }; +struct Y6 : Y5 {}; +struct Y7 : Y6 { void f() {} }; diff --git a/cpp/ql/test/query-tests/AlertSuppression/.gitattributes b/cpp/ql/test/query-tests/AlertSuppression/.gitattributes new file mode 100644 index 000000000000..03feb5510a31 --- /dev/null +++ b/cpp/ql/test/query-tests/AlertSuppression/.gitattributes @@ -0,0 +1 @@ +tstWindows.c eol=crlf diff --git a/cpp/ql/test/query-tests/AlertSuppression/tstWindows.c b/cpp/ql/test/query-tests/AlertSuppression/tstWindows.c index ee53d91dd792..a669e765981e 100644 --- a/cpp/ql/test/query-tests/AlertSuppression/tstWindows.c +++ b/cpp/ql/test/query-tests/AlertSuppression/tstWindows.c @@ -1,28 +1,28 @@ -int x = 0; // lgtm -// lgtm[js/debugger-statement] -// lgtm[js/debugger-statement, js/invocation-of-non-function] -// lgtm[@tag:nullness] -// lgtm[@tag:nullness,js/debugger-statement] -// lgtm[@expires:2017-06-11] -// lgtm[js/invocation-of-non-function] because I know better than lgtm -// lgtm: blah blah -// lgtm blah blah #falsepositive -//lgtm [js/invocation-of-non-function] -/* lgtm */ -// lgtm[] -// lgtmfoo -//lgtm -// lgtm -// lgtm [js/debugger-statement] -// foolgtm[js/debugger-statement] -// foolgtm -// foo; lgtm -// foo; lgtm[js/debugger-statement] -// foo lgtm -// foo lgtm[js/debugger-statement] -// foo lgtm bar -// foo lgtm[js/debugger-statement] bar -// LGTM! -// LGTM[js/debugger-statement] -// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function] -// lgtm[js/debugger-statement]; lgtm +int x = 0; // lgtm +// lgtm[js/debugger-statement] +// lgtm[js/debugger-statement, js/invocation-of-non-function] +// lgtm[@tag:nullness] +// lgtm[@tag:nullness,js/debugger-statement] +// lgtm[@expires:2017-06-11] +// lgtm[js/invocation-of-non-function] because I know better than lgtm +// lgtm: blah blah +// lgtm blah blah #falsepositive +//lgtm [js/invocation-of-non-function] +/* lgtm */ +// lgtm[] +// lgtmfoo +//lgtm +// lgtm +// lgtm [js/debugger-statement] +// foolgtm[js/debugger-statement] +// foolgtm +// foo; lgtm +// foo; lgtm[js/debugger-statement] +// foo lgtm +// foo lgtm[js/debugger-statement] +// foo lgtm bar +// foo lgtm[js/debugger-statement] bar +// LGTM! +// LGTM[js/debugger-statement] +// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function] +// lgtm[js/debugger-statement]; lgtm diff --git a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/functions.h b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/functions.h index 31201fb5c11e..43e7b089389b 100644 --- a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/functions.h +++ b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/functions.h @@ -1,33 +1,33 @@ - -int myFunction1(int x = - 102 + 102 + 102 + - 102 + 102 + 102 + - 102 + 102 + 102 + - 102 + 102 + 102 + - 102 + 102 + 102 + - 102 + 102 + 102 + - 102 + 102 + 102); - -void myFunction2( - int p1 = 103, - int p2 = 103, - int p3 = 103, - int p4 = 103, - int p5 = 103, - int p6 = 103, - int p7 = 103, - int p8 = 103, - int p9 = 103, - int p10 = 103, - int p11 = 103, - int p12 = 103, - int p13 = 103, - int p14 = 103, - int p15 = 103, - int p16 = 103, - int p17 = 103, - int p18 = 103, - int p19 = 103, - int p20 = 103, - int p21 = 103 - ) {}; + +int myFunction1(int x = + 102 + 102 + 102 + + 102 + 102 + 102 + + 102 + 102 + 102 + + 102 + 102 + 102 + + 102 + 102 + 102 + + 102 + 102 + 102 + + 102 + 102 + 102); + +void myFunction2( + int p1 = 103, + int p2 = 103, + int p3 = 103, + int p4 = 103, + int p5 = 103, + int p6 = 103, + int p7 = 103, + int p8 = 103, + int p9 = 103, + int p10 = 103, + int p11 = 103, + int p12 = 103, + int p13 = 103, + int p14 = 103, + int p15 = 103, + int p16 = 103, + int p17 = 103, + int p18 = 103, + int p19 = 103, + int p20 = 103, + int p21 = 103 + ) {}; diff --git a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/test.cpp b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/test.cpp index 3e331dbb34c0..268cb4e91c94 100644 --- a/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/test.cpp +++ b/cpp/ql/test/query-tests/Best Practices/Magic Constants/MagicConstantsNumbers/test.cpp @@ -1,14 +1,14 @@ - -#include "functions.h" - -int myFunction1(int x) { - return x; -} - -void myCaller() { - myFunction1(); - myFunction1(); - myFunction1(104); - myFunction2(105); - myFunction2(); + +#include "functions.h" + +int myFunction1(int x) { + return x; +} + +void myCaller() { + myFunction1(); + myFunction1(); + myFunction1(104); + myFunction2(105); + myFunction2(); } \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/a.h b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/a.h index a0da4edfc5e6..6addf5ae76d7 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/a.h +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/a.h @@ -1,3 +1,3 @@ -// a.h - -int my_func_a(); +// a.h + +int my_func_a(); diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/b.h b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/b.h index 55666fde62eb..e9f8542fdf5d 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/b.h +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/b.h @@ -1,3 +1,3 @@ -// b.h - -int my_func_b(); +// b.h + +int my_func_b(); diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/c.h b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/c.h index dfae8989d79c..f4054f52983e 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/c.h +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/c.h @@ -1,3 +1,3 @@ -// c.h - -extern int my_var_c; +// c.h + +extern int my_var_c; diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/d.hpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/d.hpp index 1a53af694b8f..ddb004892b57 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/d.hpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/d.hpp @@ -1,3 +1,3 @@ -// d.hpp - -class class_d; +// d.hpp + +class class_d; diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/e.hpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/e.hpp index 814c86a34cfe..f75c2430d0e4 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/e.hpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/e.hpp @@ -1,3 +1,3 @@ -// e.hpp - -class class_e; +// e.hpp + +class class_e; diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/f.fwd.hpp b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/f.fwd.hpp index 1eebacaf3245..ace4a0b7082b 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/f.fwd.hpp +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/f.fwd.hpp @@ -1,3 +1,3 @@ -// f.fwd.hpp - -class class_f; +// f.fwd.hpp + +class class_f; diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header1.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header1.h index ab3226d8e657..a0fa07dbb50a 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header1.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header1.h @@ -1,8 +1,8 @@ -// header1.h - -#ifndef INCLUDED_HEADER1 -#define INCLUDED_HEADER1 - - // ... - -#endif // INCLUDED_HEADER1 +// header1.h + +#ifndef INCLUDED_HEADER1 +#define INCLUDED_HEADER1 + + // ... + +#endif // INCLUDED_HEADER1 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header2.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header2.h index f9f62cc8d754..9e4ad972812b 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header2.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header2.h @@ -1,8 +1,8 @@ -// header2.h - -#ifndef INCLUDED_HEADER1 // oops! -#define INCLUDED_HEADER1 - - // ... - -#endif // INCLUDED_HEADER1 +// header2.h + +#ifndef INCLUDED_HEADER1 // oops! +#define INCLUDED_HEADER1 + + // ... + +#endif // INCLUDED_HEADER1 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header3.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header3.h index 789c3354309c..ed4e1e65ddea 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header3.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header3.h @@ -1,12 +1,12 @@ -// header3.h - -#ifndef INCLUDED_HEADER3 -#define INCLUDED_HEADER3 - - // ... - - #ifndef INCLUDED_HEADER1 // (not an include guard) - - #endif - -#endif // INCLUDED_HEADER3 +// header3.h + +#ifndef INCLUDED_HEADER3 +#define INCLUDED_HEADER3 + + // ... + + #ifndef INCLUDED_HEADER1 // (not an include guard) + + #endif + +#endif // INCLUDED_HEADER3 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header4.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header4.h index 6c990c74aa49..57b36896ebd0 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header4.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header4.h @@ -1,8 +1,8 @@ -// header4.h - -#ifndef INCLUDED_HEADER4 -#define INCLUDED_HEADER4 - - // ... - -#endif // INCLUDED_HEADER4 +// header4.h + +#ifndef INCLUDED_HEADER4 +#define INCLUDED_HEADER4 + + // ... + +#endif // INCLUDED_HEADER4 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header6.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header6.h index c8a1a968f180..2148e608917d 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header6.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header6.h @@ -1,8 +1,8 @@ -// header6.h - -#ifndef INCLUDED_HEADER6 -#define INCLUDED_HEADER6 - - // ... - -#endif // INCLUDED_HEADER6 +// header6.h + +#ifndef INCLUDED_HEADER6 +#define INCLUDED_HEADER6 + + // ... + +#endif // INCLUDED_HEADER6 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header7.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header7.h index 0687d80c60c0..4dd8875d69d8 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header7.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/header7.h @@ -1,8 +1,8 @@ -// header7.h - -#ifndef INCLUDED_HEADER6 // oops! -#define INCLUDED_HEADER6(x) (x) - - // ... - -#endif // INCLUDED_HEADER6 +// header7.h + +#ifndef INCLUDED_HEADER6 // oops! +#define INCLUDED_HEADER6(x) (x) + + // ... + +#endif // INCLUDED_HEADER6 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/main.c b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/main.c index 40cbdbb94435..0736e1517efc 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/main.c +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/main.c @@ -1,13 +1,13 @@ -// main.c - Cleanup-DuplicateIncludeGuard test - -#include "header1.h" -#include "header2.h" -#include "header3.h" -#include "header3.h" - -#include "header4.h" -#include "subfolder/header4.h" -#include "subfolder/header5.h" - -#include "header6.h" -#include "header7.h" +// main.c - Cleanup-DuplicateIncludeGuard test + +#include "header1.h" +#include "header2.h" +#include "header3.h" +#include "header3.h" + +#include "header4.h" +#include "subfolder/header4.h" +#include "subfolder/header5.h" + +#include "header6.h" +#include "header7.h" diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header4.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header4.h index f369cf90544a..c5e44813dcd2 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header4.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header4.h @@ -1,8 +1,8 @@ -// header4.h - -#ifndef INCLUDED_HEADER4 // duplicate -#define INCLUDED_HEADER4 - - // ... - -#endif // INCLUDED_HEADER4 +// header4.h + +#ifndef INCLUDED_HEADER4 // duplicate +#define INCLUDED_HEADER4 + + // ... + +#endif // INCLUDED_HEADER4 diff --git a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header5.h b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header5.h index 2f89436d7a81..ed54e7ea68cd 100644 --- a/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header5.h +++ b/cpp/ql/test/query-tests/Header Cleanup/Cleanup-DuplicateIncludeGuard/subfolder/header5.h @@ -1,8 +1,8 @@ -// header5.h - -#ifndef INCLUDED_HEADER4 // duplicate -#define INCLUDED_HEADER4 - - // ... - -#endif // INCLUDED_HEADER4 +// header5.h + +#ifndef INCLUDED_HEADER4 // duplicate +#define INCLUDED_HEADER4 + + // ... + +#endif // INCLUDED_HEADER4 diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/common.h b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/common.h index baffbb6f36f9..8dda6a8a8d34 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/common.h +++ b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/common.h @@ -1,20 +1,20 @@ - -struct _IO_FILE -{ - // ... -}; -typedef struct _IO_FILE FILE; - -#define va_list void * -#define va_start(x, y) -#define va_end(x) - -extern int printf(const char *fmt, ...); -extern int vprintf(const char *fmt, va_list ap); -extern int vfprintf(FILE *stream, const char *format, va_list ap); - -#include "printf1.h" -#include "real_world.h" -#include "wide_string.h" -#include "format.h" -#include "pri_macros.h" + +struct _IO_FILE +{ + // ... +}; +typedef struct _IO_FILE FILE; + +#define va_list void * +#define va_start(x, y) +#define va_end(x) + +extern int printf(const char *fmt, ...); +extern int vprintf(const char *fmt, va_list ap); +extern int vfprintf(FILE *stream, const char *format, va_list ap); + +#include "printf1.h" +#include "real_world.h" +#include "wide_string.h" +#include "format.h" +#include "pri_macros.h" diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/common.h b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/common.h index baffbb6f36f9..8dda6a8a8d34 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/common.h +++ b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/common.h @@ -1,20 +1,20 @@ - -struct _IO_FILE -{ - // ... -}; -typedef struct _IO_FILE FILE; - -#define va_list void * -#define va_start(x, y) -#define va_end(x) - -extern int printf(const char *fmt, ...); -extern int vprintf(const char *fmt, va_list ap); -extern int vfprintf(FILE *stream, const char *format, va_list ap); - -#include "printf1.h" -#include "real_world.h" -#include "wide_string.h" -#include "format.h" -#include "pri_macros.h" + +struct _IO_FILE +{ + // ... +}; +typedef struct _IO_FILE FILE; + +#define va_list void * +#define va_start(x, y) +#define va_end(x) + +extern int printf(const char *fmt, ...); +extern int vprintf(const char *fmt, va_list ap); +extern int vfprintf(FILE *stream, const char *format, va_list ap); + +#include "printf1.h" +#include "real_world.h" +#include "wide_string.h" +#include "format.h" +#include "pri_macros.h" diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/common.h b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/common.h index baffbb6f36f9..8dda6a8a8d34 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/common.h +++ b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/common.h @@ -1,20 +1,20 @@ - -struct _IO_FILE -{ - // ... -}; -typedef struct _IO_FILE FILE; - -#define va_list void * -#define va_start(x, y) -#define va_end(x) - -extern int printf(const char *fmt, ...); -extern int vprintf(const char *fmt, va_list ap); -extern int vfprintf(FILE *stream, const char *format, va_list ap); - -#include "printf1.h" -#include "real_world.h" -#include "wide_string.h" -#include "format.h" -#include "pri_macros.h" + +struct _IO_FILE +{ + // ... +}; +typedef struct _IO_FILE FILE; + +#define va_list void * +#define va_start(x, y) +#define va_end(x) + +extern int printf(const char *fmt, ...); +extern int vprintf(const char *fmt, va_list ap); +extern int vfprintf(FILE *stream, const char *format, va_list ap); + +#include "printf1.h" +#include "real_world.h" +#include "wide_string.h" +#include "format.h" +#include "pri_macros.h" diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft_no_wchar/common.h b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft_no_wchar/common.h index baffbb6f36f9..8dda6a8a8d34 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft_no_wchar/common.h +++ b/cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft_no_wchar/common.h @@ -1,20 +1,20 @@ - -struct _IO_FILE -{ - // ... -}; -typedef struct _IO_FILE FILE; - -#define va_list void * -#define va_start(x, y) -#define va_end(x) - -extern int printf(const char *fmt, ...); -extern int vprintf(const char *fmt, va_list ap); -extern int vfprintf(FILE *stream, const char *format, va_list ap); - -#include "printf1.h" -#include "real_world.h" -#include "wide_string.h" -#include "format.h" -#include "pri_macros.h" + +struct _IO_FILE +{ + // ... +}; +typedef struct _IO_FILE FILE; + +#define va_list void * +#define va_start(x, y) +#define va_end(x) + +extern int printf(const char *fmt, ...); +extern int vprintf(const char *fmt, va_list ap); +extern int vfprintf(FILE *stream, const char *format, va_list ap); + +#include "printf1.h" +#include "real_world.h" +#include "wide_string.h" +#include "format.h" +#include "pri_macros.h" diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StackAddressEscapes/manager.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StackAddressEscapes/manager.cpp index 843ffb340be2..8b73bffb04ab 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StackAddressEscapes/manager.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StackAddressEscapes/manager.cpp @@ -1,54 +1,54 @@ -// test cases for StackAddressEscapes.ql - -namespace std -{ - class string; - - template class vector - { - }; -}; - -class manager -{ -public: - manager() {}; - ~manager() {}; -}; - -class resource -{ -public: - resource(manager *_m) : m(_m) {}; - - void set_strings(std::vector const &_strings); - -private: - manager *m; - std::vector const *strings; -}; - -void resource :: set_strings(std::vector const &_strings) -{ - strings = &_strings; -} - -manager *glob_man; - -manager *test_managers() -{ - manager man; - manager *man_ptr; - man_ptr = &man; - - resource a(&man); // BAD: stack address `&man` escapes [NOT DETECTED] - resource b(man_ptr); // BAD: stack address `man_ptr` escapes [NOT DETECTED] - resource *c = new resource(&man); // BAD: stack address `&man` escapes [NOT DETECTED] - - std::vector vs; - a.set_strings(vs); // BAD: stack address `&vs` escapes [NOT DETECTED] - - glob_man = &man; // BAD: stack address `&man` escapes - - return &man; // BAD: stack address `&man` escapes [NOT DETECTED] -} +// test cases for StackAddressEscapes.ql + +namespace std +{ + class string; + + template class vector + { + }; +}; + +class manager +{ +public: + manager() {}; + ~manager() {}; +}; + +class resource +{ +public: + resource(manager *_m) : m(_m) {}; + + void set_strings(std::vector const &_strings); + +private: + manager *m; + std::vector const *strings; +}; + +void resource :: set_strings(std::vector const &_strings) +{ + strings = &_strings; +} + +manager *glob_man; + +manager *test_managers() +{ + manager man; + manager *man_ptr; + man_ptr = &man; + + resource a(&man); // BAD: stack address `&man` escapes [NOT DETECTED] + resource b(man_ptr); // BAD: stack address `man_ptr` escapes [NOT DETECTED] + resource *c = new resource(&man); // BAD: stack address `&man` escapes [NOT DETECTED] + + std::vector vs; + a.set_strings(vs); // BAD: stack address `&vs` escapes [NOT DETECTED] + + glob_man = &man; // BAD: stack address `&man` escapes + + return &man; // BAD: stack address `&man` escapes [NOT DETECTED] +} diff --git a/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.CPP b/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.CPP index c17c5ea6e5f8..04637aa9c90f 100644 --- a/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.CPP +++ b/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.CPP @@ -1,2 +1,2 @@ -#include "test.c++" -#include "test.hpp" +#include "test.c++" +#include "test.hpp" diff --git a/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.c b/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.c index 1da91c1cfdac..47c2408c2fb9 100644 --- a/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.c +++ b/cpp/ql/test/query-tests/jsf/4.09 Style/AV Rule 53 54/test.c @@ -1,4 +1,4 @@ -#include "test" -#include "test.abc" -#include "test.H" -#include "test'.h" +#include "test" +#include "test.abc" +#include "test.H" +#include "test'.h" diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 73/original.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 73/original.cpp index 78001340db43..5c86702e2c26 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 73/original.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 73/original.cpp @@ -1,160 +1,160 @@ -// bad: gratuitous default constructor -class Bad -{ -private: - int key; - int value; -public: - Bad(); - Bad(int); - Bad(int, int); - int cmp(const Bad& that); -}; - -Bad::Bad() : key(-1) // non-compliant -{ -} - -Bad::Bad(int k) : key(k) // compliant -{ -} - -Bad::Bad(int k, int v) // compliant -{ - key = k; - value = v; -} - -int Bad::cmp(const Bad& that) -{ - if(this->key == -1) - return 1; - if(that.key == -1) - return -1; - return this->key - that.key; -} - -// good: default constructor is necessary because we allocate an array of Good -class Good -{ -private: - char *cp; -public: - Good(); - Good(char *const cpp); - char getChar(); -}; - -Good::Good() : cp(0) // compliant -{ -} - -Good::Good(char *const cpp) : cp(cpp) -{ -} - -char Good::getChar() -{ - if(cp == 0) - return '\0'; - return *cp; -} - -Good *gd = new Good[16]; - -// good: default constructor is necessary because we instantiate a template with AlsoGood -class AlsoGood -{ -private: - char *cp; -public: - AlsoGood(); - AlsoGood(char *const cpp); - char getChar(); -}; - -AlsoGood::AlsoGood() // compliant [FALSE POSITIVE] -{ - cp = 0; -} - -AlsoGood::AlsoGood(char *const cpp) : cp(cpp) -{ -} - -char AlsoGood::getChar() -{ - if(cp == 0) - return '\0'; - return *cp; -} - -template class Container { -private: - T *data; -public: - Container(); -}; - -template Container::Container() -{ - data = new T(); -} - -Container *foo; - -// good: default constructor is convenient since StillGood is a virtual base class -class StillGood -{ -private: - char *cp; -public: - StillGood(); - StillGood(char *const cpp); - char getChar(); -}; - -StillGood::StillGood() : cp(0) // compliant -{ -} - -StillGood::StillGood(char *const cpp) : cp(cpp) -{ -} - -char StillGood::getChar() -{ - if(cp == 0) - return '\0'; - return *cp; -} - -class Child : public virtual StillGood -{ -}; - -double sqrt(double d); - -// good: members have sensible default values -class Coord -{ -private: - double x, y; -public: - Coord(); - Coord(double, double); - double dist(); -}; - -Coord::Coord() : x(0), y(0) // compliant -{ -} - -Coord::Coord(double x, double y) : x(x), y(y) -{ -} - -double Coord::dist() -{ - return sqrt(x*x+y*y); -} +// bad: gratuitous default constructor +class Bad +{ +private: + int key; + int value; +public: + Bad(); + Bad(int); + Bad(int, int); + int cmp(const Bad& that); +}; + +Bad::Bad() : key(-1) // non-compliant +{ +} + +Bad::Bad(int k) : key(k) // compliant +{ +} + +Bad::Bad(int k, int v) // compliant +{ + key = k; + value = v; +} + +int Bad::cmp(const Bad& that) +{ + if(this->key == -1) + return 1; + if(that.key == -1) + return -1; + return this->key - that.key; +} + +// good: default constructor is necessary because we allocate an array of Good +class Good +{ +private: + char *cp; +public: + Good(); + Good(char *const cpp); + char getChar(); +}; + +Good::Good() : cp(0) // compliant +{ +} + +Good::Good(char *const cpp) : cp(cpp) +{ +} + +char Good::getChar() +{ + if(cp == 0) + return '\0'; + return *cp; +} + +Good *gd = new Good[16]; + +// good: default constructor is necessary because we instantiate a template with AlsoGood +class AlsoGood +{ +private: + char *cp; +public: + AlsoGood(); + AlsoGood(char *const cpp); + char getChar(); +}; + +AlsoGood::AlsoGood() // compliant [FALSE POSITIVE] +{ + cp = 0; +} + +AlsoGood::AlsoGood(char *const cpp) : cp(cpp) +{ +} + +char AlsoGood::getChar() +{ + if(cp == 0) + return '\0'; + return *cp; +} + +template class Container { +private: + T *data; +public: + Container(); +}; + +template Container::Container() +{ + data = new T(); +} + +Container *foo; + +// good: default constructor is convenient since StillGood is a virtual base class +class StillGood +{ +private: + char *cp; +public: + StillGood(); + StillGood(char *const cpp); + char getChar(); +}; + +StillGood::StillGood() : cp(0) // compliant +{ +} + +StillGood::StillGood(char *const cpp) : cp(cpp) +{ +} + +char StillGood::getChar() +{ + if(cp == 0) + return '\0'; + return *cp; +} + +class Child : public virtual StillGood +{ +}; + +double sqrt(double d); + +// good: members have sensible default values +class Coord +{ +private: + double x, y; +public: + Coord(); + Coord(double, double); + double dist(); +}; + +Coord::Coord() : x(0), y(0) // compliant +{ +} + +Coord::Coord(double x, double y) : x(x), y(y) +{ +} + +double Coord::dist() +{ + return sqrt(x*x+y*y); +} diff --git a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 78/AV Rule 78.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 78/AV Rule 78.cpp index 08442008309d..7612ac07c8ce 100644 --- a/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 78/AV Rule 78.cpp +++ b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 78/AV Rule 78.cpp @@ -1,108 +1,108 @@ -struct HasDtor -{ - ~HasDtor(); -}; - -struct Base_NonVirtual_NoDtor -{ - void NonVirtualFunction(); -}; - -struct Base_NonVirtual_VirtualDtor -{ - virtual ~Base_NonVirtual_VirtualDtor(); - void NonVirtualFunction(); -}; - -struct Base_NonVirtual_NonVirtualDtor -{ - ~Base_NonVirtual_NonVirtualDtor(); - void NonVirtualFunction(); -}; - -struct Base_NonVirtual_ImplicitDtor -{ - HasDtor m_hasDtor; - void NonVirtualFunction(); -}; - -struct Derived_NonVirtual_NoDtor : public Base_NonVirtual_NoDtor -{ -}; - -struct Derived_NonVirtual_VirtualDtor : public Base_NonVirtual_VirtualDtor -{ -}; - -struct Derived_NonVirtual_NonVirtualDtor : public Base_NonVirtual_NonVirtualDtor -{ -}; - -struct Derived_NonVirtual_ImplicitDtor : public Base_NonVirtual_ImplicitDtor -{ -}; - -struct Base_Virtual_NoDtor -{ - virtual void VirtualFunction(); -}; - -struct Base_Virtual_VirtualDtor -{ - virtual ~Base_Virtual_VirtualDtor(); - virtual void VirtualFunction(); -}; - -struct Base_Virtual_NonVirtualDtor -{ - ~Base_Virtual_NonVirtualDtor(); - virtual void VirtualFunction(); -}; - -struct Base_Virtual_ImplicitDtor -{ - HasDtor m_hasDtor; - virtual void VirtualFunction(); -}; - -struct Base_Virtual_NonVirtualDtorWithDefinition -{ - ~Base_Virtual_NonVirtualDtorWithDefinition(); - virtual void VirtualFunction(); -}; - -Base_Virtual_NonVirtualDtorWithDefinition::~Base_Virtual_NonVirtualDtorWithDefinition() -{ -} - -struct Base_Virtual_NonVirtualDtorWithInlineDefinition -{ - ~Base_Virtual_NonVirtualDtorWithInlineDefinition() - { - } - virtual void VirtualFunction(); -}; - -struct Derived_Virtual_NoDtor : public Base_Virtual_NoDtor -{ -}; - -struct Derived_Virtual_VirtualDtor : public Base_Virtual_VirtualDtor -{ -}; - -struct Derived_Virtual_NonVirtualDtor : public Base_Virtual_NonVirtualDtor -{ -}; - -struct Derived_Virtual_ImplicitDtor : public Base_Virtual_ImplicitDtor -{ -}; - -struct Derived_Virtual_NonVirtualDtorWithDefinition: public Base_Virtual_NonVirtualDtorWithDefinition -{ -}; - -struct Derived_Virtual_NonVirtualDtorWithInlineDefinition: public Base_Virtual_NonVirtualDtorWithInlineDefinition -{ -}; +struct HasDtor +{ + ~HasDtor(); +}; + +struct Base_NonVirtual_NoDtor +{ + void NonVirtualFunction(); +}; + +struct Base_NonVirtual_VirtualDtor +{ + virtual ~Base_NonVirtual_VirtualDtor(); + void NonVirtualFunction(); +}; + +struct Base_NonVirtual_NonVirtualDtor +{ + ~Base_NonVirtual_NonVirtualDtor(); + void NonVirtualFunction(); +}; + +struct Base_NonVirtual_ImplicitDtor +{ + HasDtor m_hasDtor; + void NonVirtualFunction(); +}; + +struct Derived_NonVirtual_NoDtor : public Base_NonVirtual_NoDtor +{ +}; + +struct Derived_NonVirtual_VirtualDtor : public Base_NonVirtual_VirtualDtor +{ +}; + +struct Derived_NonVirtual_NonVirtualDtor : public Base_NonVirtual_NonVirtualDtor +{ +}; + +struct Derived_NonVirtual_ImplicitDtor : public Base_NonVirtual_ImplicitDtor +{ +}; + +struct Base_Virtual_NoDtor +{ + virtual void VirtualFunction(); +}; + +struct Base_Virtual_VirtualDtor +{ + virtual ~Base_Virtual_VirtualDtor(); + virtual void VirtualFunction(); +}; + +struct Base_Virtual_NonVirtualDtor +{ + ~Base_Virtual_NonVirtualDtor(); + virtual void VirtualFunction(); +}; + +struct Base_Virtual_ImplicitDtor +{ + HasDtor m_hasDtor; + virtual void VirtualFunction(); +}; + +struct Base_Virtual_NonVirtualDtorWithDefinition +{ + ~Base_Virtual_NonVirtualDtorWithDefinition(); + virtual void VirtualFunction(); +}; + +Base_Virtual_NonVirtualDtorWithDefinition::~Base_Virtual_NonVirtualDtorWithDefinition() +{ +} + +struct Base_Virtual_NonVirtualDtorWithInlineDefinition +{ + ~Base_Virtual_NonVirtualDtorWithInlineDefinition() + { + } + virtual void VirtualFunction(); +}; + +struct Derived_Virtual_NoDtor : public Base_Virtual_NoDtor +{ +}; + +struct Derived_Virtual_VirtualDtor : public Base_Virtual_VirtualDtor +{ +}; + +struct Derived_Virtual_NonVirtualDtor : public Base_Virtual_NonVirtualDtor +{ +}; + +struct Derived_Virtual_ImplicitDtor : public Base_Virtual_ImplicitDtor +{ +}; + +struct Derived_Virtual_NonVirtualDtorWithDefinition: public Base_Virtual_NonVirtualDtorWithDefinition +{ +}; + +struct Derived_Virtual_NonVirtualDtorWithInlineDefinition: public Base_Virtual_NonVirtualDtorWithInlineDefinition +{ +}; diff --git a/cpp/ql/test/successor-tests/assignexpr/assignexpr.cpp b/cpp/ql/test/successor-tests/assignexpr/assignexpr.cpp index 1bd6c572eecb..b1ceec882828 100644 --- a/cpp/ql/test/successor-tests/assignexpr/assignexpr.cpp +++ b/cpp/ql/test/successor-tests/assignexpr/assignexpr.cpp @@ -1,11 +1,11 @@ -class C { - public: - int i; -}; - -void f() { - C c; - int a, b; - c.i = a + b; - ; -} +class C { + public: + int i; +}; + +void f() { + C c; + int a, b; + c.i = a + b; + ; +} diff --git a/cpp/ql/test/successor-tests/block/dummyblock/dummyblock.c b/cpp/ql/test/successor-tests/block/dummyblock/dummyblock.c index f113a35f204c..1307903ecf5b 100644 --- a/cpp/ql/test/successor-tests/block/dummyblock/dummyblock.c +++ b/cpp/ql/test/successor-tests/block/dummyblock/dummyblock.c @@ -1,4 +1,4 @@ -void f() { - if (1) - ; -} +void f() { + if (1) + ; +} diff --git a/cpp/ql/test/successor-tests/block/emptyblock/emptyblock.c b/cpp/ql/test/successor-tests/block/emptyblock/emptyblock.c index 87680af388dc..33ef4b879cd6 100644 --- a/cpp/ql/test/successor-tests/block/emptyblock/emptyblock.c +++ b/cpp/ql/test/successor-tests/block/emptyblock/emptyblock.c @@ -1,5 +1,5 @@ -void f() { - { - } - ; -} +void f() { + { + } + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/args/membercallexpr/membercallexpr.cpp b/cpp/ql/test/successor-tests/callexpr/args/membercallexpr/membercallexpr.cpp index 9d4edb8979bf..5009f9132555 100644 --- a/cpp/ql/test/successor-tests/callexpr/args/membercallexpr/membercallexpr.cpp +++ b/cpp/ql/test/successor-tests/callexpr/args/membercallexpr/membercallexpr.cpp @@ -1,11 +1,11 @@ -class C { - public: - C* d; - void g(int x, int y); -}; - -void f() { - int i, j, k, l; - C *c; - c->d->g(i + j, k - l); -} +class C { + public: + C* d; + void g(int x, int y); +}; + +void f() { + int i, j, k, l; + C *c; + c->d->g(i + j, k - l); +} diff --git a/cpp/ql/test/successor-tests/callexpr/args/staticmembercallexpr/staticmembercallexpr.cpp b/cpp/ql/test/successor-tests/callexpr/args/staticmembercallexpr/staticmembercallexpr.cpp index 10acb0fbc1f6..df553720daba 100644 --- a/cpp/ql/test/successor-tests/callexpr/args/staticmembercallexpr/staticmembercallexpr.cpp +++ b/cpp/ql/test/successor-tests/callexpr/args/staticmembercallexpr/staticmembercallexpr.cpp @@ -1,11 +1,11 @@ -class C { - public: - C* d; - static void g(int x, int y); -}; - -void f() { - int i, j, k, l; - C c; - c.d->g(i + j, k - l); -} +class C { + public: + C* d; + static void g(int x, int y); +}; + +void f() { + int i, j, k, l; + C c; + c.d->g(i + j, k - l); +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/membercallexpr/membercallexpr.cpp b/cpp/ql/test/successor-tests/callexpr/noargs/membercallexpr/membercallexpr.cpp index 1548f3f81a72..035db2d5856f 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/membercallexpr/membercallexpr.cpp +++ b/cpp/ql/test/successor-tests/callexpr/noargs/membercallexpr/membercallexpr.cpp @@ -1,10 +1,10 @@ -class C { - public: - void g(); -}; - -void f() { - C *c; - c->g(); - ; -} +class C { + public: + void g(); +}; + +void f() { + C *c; + c->g(); + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/nonmembercallexpr/nonmembercallexpr.c b/cpp/ql/test/successor-tests/callexpr/noargs/nonmembercallexpr/nonmembercallexpr.c index fc37abcdfdb0..4c9499249001 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/nonmembercallexpr/nonmembercallexpr.c +++ b/cpp/ql/test/successor-tests/callexpr/noargs/nonmembercallexpr/nonmembercallexpr.c @@ -1,6 +1,6 @@ -void g() { } - -void f() { - g(); - ; -} +void g() { } + +void f() { + g(); + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfp2callexpr/nonmemberfp2callexpr.c b/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfp2callexpr/nonmemberfp2callexpr.c index b04b51fcb14f..9e458f2f50d9 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfp2callexpr/nonmemberfp2callexpr.c +++ b/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfp2callexpr/nonmemberfp2callexpr.c @@ -1,6 +1,6 @@ -void (*g())(); - -void f() { - g()(); - ; -} +void (*g())(); + +void f() { + g()(); + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfpcallexpr/nonmemberfpcallexpr.c b/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfpcallexpr/nonmemberfpcallexpr.c index e22a8d4a1a07..51e06cec04be 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfpcallexpr/nonmemberfpcallexpr.c +++ b/cpp/ql/test/successor-tests/callexpr/noargs/nonmemberfpcallexpr/nonmemberfpcallexpr.c @@ -1,5 +1,5 @@ -void f() { - int (*g)(); - g(); - ; -} +void f() { + int (*g)(); + g(); + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/pmcallexpr/pmcallexpr.cpp b/cpp/ql/test/successor-tests/callexpr/noargs/pmcallexpr/pmcallexpr.cpp index 76c085f8ec0f..75e143d846a4 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/pmcallexpr/pmcallexpr.cpp +++ b/cpp/ql/test/successor-tests/callexpr/noargs/pmcallexpr/pmcallexpr.cpp @@ -1,10 +1,10 @@ -class C { - public: - void (C::*g)(); -}; - -void f() { - C *c, *d; - (c->*(d->g))(); - ; -} +class C { + public: + void (C::*g)(); +}; + +void f() { + C *c, *d; + (c->*(d->g))(); + ; +} diff --git a/cpp/ql/test/successor-tests/callexpr/noargs/staticmembercallexpr/staticmembercallexpr.cpp b/cpp/ql/test/successor-tests/callexpr/noargs/staticmembercallexpr/staticmembercallexpr.cpp index 0b744c9d1c1e..216b450e0dd9 100644 --- a/cpp/ql/test/successor-tests/callexpr/noargs/staticmembercallexpr/staticmembercallexpr.cpp +++ b/cpp/ql/test/successor-tests/callexpr/noargs/staticmembercallexpr/staticmembercallexpr.cpp @@ -1,10 +1,10 @@ -class C { - public: - static void g(); -}; - -void f() { - C c; - c.g(); - ; -} +class C { + public: + static void g(); +}; + +void f() { + C c; + c.g(); + ; +} diff --git a/cpp/ql/test/successor-tests/deleteexpr/defdestructordeleteexpr/defdestructordeleteexpr.cpp b/cpp/ql/test/successor-tests/deleteexpr/defdestructordeleteexpr/defdestructordeleteexpr.cpp index ce92915640f9..213898543a69 100644 --- a/cpp/ql/test/successor-tests/deleteexpr/defdestructordeleteexpr/defdestructordeleteexpr.cpp +++ b/cpp/ql/test/successor-tests/deleteexpr/defdestructordeleteexpr/defdestructordeleteexpr.cpp @@ -1,6 +1,6 @@ -class C { }; - -void f() { - C* c = new C(); - delete c; -} +class C { }; + +void f() { + C* c = new C(); + delete c; +} diff --git a/cpp/ql/test/successor-tests/deleteexpr/deleteexpr/deleteexpr.cpp b/cpp/ql/test/successor-tests/deleteexpr/deleteexpr/deleteexpr.cpp index 1b22a4736b18..5e997100e37a 100644 --- a/cpp/ql/test/successor-tests/deleteexpr/deleteexpr/deleteexpr.cpp +++ b/cpp/ql/test/successor-tests/deleteexpr/deleteexpr/deleteexpr.cpp @@ -1,9 +1,9 @@ -class C { - public: - ~C(); -}; - -void f() { - C* c = new C(); - delete c; -} +class C { + public: + ~C(); +}; + +void f() { + C* c = new C(); + delete c; +} diff --git a/cpp/ql/test/successor-tests/dostmt/dostmt.c b/cpp/ql/test/successor-tests/dostmt/dostmt.c index 1b8f4ed2e69e..5f1d14488dd3 100644 --- a/cpp/ql/test/successor-tests/dostmt/dostmt.c +++ b/cpp/ql/test/successor-tests/dostmt/dostmt.c @@ -1,38 +1,38 @@ -void always_false() { - do { - l1:; - } while(0); - l2:; -} - -void always_true_1() { - do { - l1:; - break; - } while(1); - l2:; -} - -void always_true_2() { - do { - l1:; - break; - l2:; - } while(1); - l3:; -} - -void always_true_3() { - do { - l1:; - } while(1); - l2:; -} - -void normal() { - int i = 0; - do { - ++i; - } while(i < 10); - l:; -} +void always_false() { + do { + l1:; + } while(0); + l2:; +} + +void always_true_1() { + do { + l1:; + break; + } while(1); + l2:; +} + +void always_true_2() { + do { + l1:; + break; + l2:; + } while(1); + l3:; +} + +void always_true_3() { + do { + l1:; + } while(1); + l2:; +} + +void normal() { + int i = 0; + do { + ++i; + } while(i < 10); + l:; +} diff --git a/cpp/ql/test/successor-tests/enum/enum.c b/cpp/ql/test/successor-tests/enum/enum.c index dd5be9ec5950..4e714c7c9405 100644 --- a/cpp/ql/test/successor-tests/enum/enum.c +++ b/cpp/ql/test/successor-tests/enum/enum.c @@ -1,7 +1,7 @@ -enum { - a = 1 + 1 -}; - -int f() { - return a; -} +enum { + a = 1 + 1 +}; + +int f() { + return a; +} diff --git a/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/ellipsisexceptionhandler.cpp b/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/ellipsisexceptionhandler.cpp index 8c6f2a7cb398..97666c3bf85a 100644 --- a/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/ellipsisexceptionhandler.cpp +++ b/cpp/ql/test/successor-tests/exceptionhandler/ellipsisexceptionhandler/ellipsisexceptionhandler.cpp @@ -1,17 +1,17 @@ -void f() { - try { - try { - throw 1; - } catch (int i) { - } catch (...) { - } - } catch (int j) { - } -} - -void g(bool condition) { - try { - if (condition) throw 1; - } catch (...) { - } -} +void f() { + try { + try { + throw 1; + } catch (int i) { + } catch (...) { + } + } catch (int j) { + } +} + +void g(bool condition) { + try { + if (condition) throw 1; + } catch (...) { + } +} diff --git a/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/exceptionhandler.cpp b/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/exceptionhandler.cpp index e5818c89125a..66c20661fd19 100644 --- a/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/exceptionhandler.cpp +++ b/cpp/ql/test/successor-tests/exceptionhandler/exceptionhandler/exceptionhandler.cpp @@ -1,22 +1,22 @@ -class C { }; -class D { }; - -void g() { - throw 1; -} - -void f() { - try { - try { - g(); - throw 2; -l: - } catch (int) { - 4; - } - } catch (C) { - 5; - } catch (D) { - 6; - } -} +class C { }; +class D { }; + +void g() { + throw 1; +} + +void f() { + try { + try { + g(); + throw 2; +l: + } catch (int) { + 4; + } + } catch (C) { + 5; + } catch (D) { + 6; + } +} diff --git a/cpp/ql/test/successor-tests/exprstmt/exprstmt.c b/cpp/ql/test/successor-tests/exprstmt/exprstmt.c index 745dee0c47dc..a0ce2659424f 100644 --- a/cpp/ql/test/successor-tests/exprstmt/exprstmt.c +++ b/cpp/ql/test/successor-tests/exprstmt/exprstmt.c @@ -1,4 +1,4 @@ -void f() { - 1; - ; -} +void f() { + 1; + ; +} diff --git a/cpp/ql/test/successor-tests/fieldaccessexpr/constmemberaccess/constmemberaccess.cpp b/cpp/ql/test/successor-tests/fieldaccessexpr/constmemberaccess/constmemberaccess.cpp index 74445da5a997..9a75ab5b2e97 100644 --- a/cpp/ql/test/successor-tests/fieldaccessexpr/constmemberaccess/constmemberaccess.cpp +++ b/cpp/ql/test/successor-tests/fieldaccessexpr/constmemberaccess/constmemberaccess.cpp @@ -1,10 +1,10 @@ -class C { - public: - int x; -}; - -void f() { - C *c; - int i; - i = c->x; +class C { + public: + int x; +}; + +void f() { + C *c; + int i; + i = c->x; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/fieldaccessexpr/fieldaccess/fieldaccess.cpp b/cpp/ql/test/successor-tests/fieldaccessexpr/fieldaccess/fieldaccess.cpp index 74445da5a997..9a75ab5b2e97 100644 --- a/cpp/ql/test/successor-tests/fieldaccessexpr/fieldaccess/fieldaccess.cpp +++ b/cpp/ql/test/successor-tests/fieldaccessexpr/fieldaccess/fieldaccess.cpp @@ -1,10 +1,10 @@ -class C { - public: - int x; -}; - -void f() { - C *c; - int i; - i = c->x; +class C { + public: + int x; +}; + +void f() { + C *c; + int i; + i = c->x; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/forstmt/forstmt/forstmt.cpp b/cpp/ql/test/successor-tests/forstmt/forstmt/forstmt.cpp index 2ccbd6b3f544..917ccb0bbfdd 100644 --- a/cpp/ql/test/successor-tests/forstmt/forstmt/forstmt.cpp +++ b/cpp/ql/test/successor-tests/forstmt/forstmt/forstmt.cpp @@ -1,21 +1,21 @@ -void f1() { - for (int i = 0; i < 10; i++) { - l1: - } - ; -} - -void f2() { - for (int i = 0; false; i++) { // true edge pruned - } -} - -void f3() { - for (int i = 0; true; i++) { // false edge pruned - } -} - -void f4() { - for (int i = 0; i < 0; i++) { // true edge pruned - } +void f1() { + for (int i = 0; i < 10; i++) { + l1: + } + ; +} + +void f2() { + for (int i = 0; false; i++) { // true edge pruned + } +} + +void f3() { + for (int i = 0; true; i++) { // false edge pruned + } +} + +void f4() { + for (int i = 0; i < 0; i++) { // true edge pruned + } } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt.cpp b/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt.cpp index e5257a9db53e..d0587d8e85b5 100644 --- a/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt.cpp +++ b/cpp/ql/test/successor-tests/forstmt/shortforstmt/shortforstmt.cpp @@ -1,38 +1,38 @@ -void always_false() { - for(;0;) { - l1:; - } - l2:; -} - -void always_true_1() { - for(;1;) { - l1:; - break; - } - l2:; -} - -void always_true_2() { - for(;1;) { - l1:; - if(0) break; - if(1) break; - l2:; - } - l3:; -} - -void always_true_3() { - for(;1;) { - l1:; - } - l2:; -} - -void normal(int x, int y) { - for(;x < y;) { - x = y; - } - l:; -} +void always_false() { + for(;0;) { + l1:; + } + l2:; +} + +void always_true_1() { + for(;1;) { + l1:; + break; + } + l2:; +} + +void always_true_2() { + for(;1;) { + l1:; + if(0) break; + if(1) break; + l2:; + } + l3:; +} + +void always_true_3() { + for(;1;) { + l1:; + } + l2:; +} + +void normal(int x, int y) { + for(;x < y;) { + x = y; + } + l:; +} diff --git a/cpp/ql/test/successor-tests/forstmt/tinyforstmt/tinyforstmt.c b/cpp/ql/test/successor-tests/forstmt/tinyforstmt/tinyforstmt.c index 129e2b279b51..cbb6465ab7b6 100644 --- a/cpp/ql/test/successor-tests/forstmt/tinyforstmt/tinyforstmt.c +++ b/cpp/ql/test/successor-tests/forstmt/tinyforstmt/tinyforstmt.c @@ -1,5 +1,5 @@ -void f() { - for (;;) { - ; - } -} +void f() { + for (;;) { + ; + } +} diff --git a/cpp/ql/test/successor-tests/gotostmt/gotostmt.c b/cpp/ql/test/successor-tests/gotostmt/gotostmt.c index 318e26caf69f..4a5e44f4b00f 100644 --- a/cpp/ql/test/successor-tests/gotostmt/gotostmt.c +++ b/cpp/ql/test/successor-tests/gotostmt/gotostmt.c @@ -1,5 +1,5 @@ -void v() { - goto label; - ; -label: -} +void v() { + goto label; + ; +label: +} diff --git a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt.c b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt.c index 618b67dc7898..c21e213c8c7e 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt.c +++ b/cpp/ql/test/successor-tests/ifstmt/ifelsestmt/ifelsestmt.c @@ -1,45 +1,45 @@ -void always_false_1() { - if(0) { - l1:; - } - else { - l2:; - } - l3:; -} - -void always_false_2() { - if(0) - l1:; - else - l2:; - l3:; -} - -void always_true_1() { - if(1) { - l1:; - } - else { - l2:; - } - l3:; -} - -void always_true_2() { - if(1) - l1:; - else - l2:; - l3:; -} - -void normal(int x, int y) { - if(x == y) { - l1:; - } - else { - l2:; - } - l3:; -} +void always_false_1() { + if(0) { + l1:; + } + else { + l2:; + } + l3:; +} + +void always_false_2() { + if(0) + l1:; + else + l2:; + l3:; +} + +void always_true_1() { + if(1) { + l1:; + } + else { + l2:; + } + l3:; +} + +void always_true_2() { + if(1) + l1:; + else + l2:; + l3:; +} + +void normal(int x, int y) { + if(x == y) { + l1:; + } + else { + l2:; + } + l3:; +} diff --git a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.c b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.c index a98a1bfffe7b..cf8c969277d6 100644 --- a/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.c +++ b/cpp/ql/test/successor-tests/ifstmt/ifstmt/ifstmt.c @@ -1,32 +1,32 @@ -void always_false_1() { - if(0) { - l1:; - } - l2:; -} - -void always_false_2() { - if(0) - l1:; - l2:; -} - -void always_true_1() { - if(1) { - l1:; - } - l2:; -} - -void always_true_2() { - if(1) - l1:; - l2:; -} - -void normal(int x, int y) { - if(x == y) { - l1:; - } - l2:; -} +void always_false_1() { + if(0) { + l1:; + } + l2:; +} + +void always_false_2() { + if(0) + l1:; + l2:; +} + +void always_true_1() { + if(1) { + l1:; + } + l2:; +} + +void always_true_2() { + if(1) + l1:; + l2:; +} + +void normal(int x, int y) { + if(x == y) { + l1:; + } + l2:; +} diff --git a/cpp/ql/test/successor-tests/initializer/aggregateinitializer/aggregateinitializer.c b/cpp/ql/test/successor-tests/initializer/aggregateinitializer/aggregateinitializer.c index 2c665328a18c..463bd49e0a0c 100644 --- a/cpp/ql/test/successor-tests/initializer/aggregateinitializer/aggregateinitializer.c +++ b/cpp/ql/test/successor-tests/initializer/aggregateinitializer/aggregateinitializer.c @@ -1,4 +1,4 @@ -void f() { - int a, b, c, d; - int x[] = { a + b, c - d }; -} +void f() { + int a, b, c, d; + int x[] = { a + b, c - d }; +} diff --git a/cpp/ql/test/successor-tests/initializer/constructorinitializer/constructorinitializer.cpp b/cpp/ql/test/successor-tests/initializer/constructorinitializer/constructorinitializer.cpp index adb3460317bf..ce43dedf6ed4 100644 --- a/cpp/ql/test/successor-tests/initializer/constructorinitializer/constructorinitializer.cpp +++ b/cpp/ql/test/successor-tests/initializer/constructorinitializer/constructorinitializer.cpp @@ -1,9 +1,9 @@ -class C { - public: - C(int x, int y); -}; - -void f() { - int i, j, k, l; - C c(i + j, k - l); -} +class C { + public: + C(int x, int y); +}; + +void f() { + int i, j, k, l; + C c(i + j, k - l); +} diff --git a/cpp/ql/test/successor-tests/initializer/initializer/initializer.c b/cpp/ql/test/successor-tests/initializer/initializer/initializer.c index 40204e94bf9d..5cd9949f4c3c 100644 --- a/cpp/ql/test/successor-tests/initializer/initializer/initializer.c +++ b/cpp/ql/test/successor-tests/initializer/initializer/initializer.c @@ -1,4 +1,4 @@ -void f() { - int a, b; - int i = a + b; +void f() { + int a, b; + int i = a + b; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/initializer/parameterinitializer/parameterinitializer.cpp b/cpp/ql/test/successor-tests/initializer/parameterinitializer/parameterinitializer.cpp index 35ffb855f8ac..d132cd810a01 100644 --- a/cpp/ql/test/successor-tests/initializer/parameterinitializer/parameterinitializer.cpp +++ b/cpp/ql/test/successor-tests/initializer/parameterinitializer/parameterinitializer.cpp @@ -1,31 +1,31 @@ -void printf(char *format, ...); - -int g(void) { - return 5; -} - -void f(int i = g()) { - printf("Got %d\n", i); -} - -class c { -public: - c(int j = g()) {}; - - void method(int k = g()) {}; -}; - -int main(void) { - f(3); - f(); - f(4); - f(); - - { - c my_c; - - my_c.method(); - } - - return 0; -} +void printf(char *format, ...); + +int g(void) { + return 5; +} + +void f(int i = g()) { + printf("Got %d\n", i); +} + +class c { +public: + c(int j = g()) {}; + + void method(int k = g()) {}; +}; + +int main(void) { + f(3); + f(); + f(4); + f(); + + { + c my_c; + + my_c.method(); + } + + return 0; +} diff --git a/cpp/ql/test/successor-tests/landexpr/landexpr.c b/cpp/ql/test/successor-tests/landexpr/landexpr.c index 90e62d1b58f6..98fa0a23c6d7 100644 --- a/cpp/ql/test/successor-tests/landexpr/landexpr.c +++ b/cpp/ql/test/successor-tests/landexpr/landexpr.c @@ -1,6 +1,6 @@ -void f() { - int a, b; - if (a && b) { - } - ; +void f() { + int a, b; + if (a && b) { + } + ; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/lorexpr/lorexpr.c b/cpp/ql/test/successor-tests/lorexpr/lorexpr.c index 81e88d1ca27c..d03d89760b60 100644 --- a/cpp/ql/test/successor-tests/lorexpr/lorexpr.c +++ b/cpp/ql/test/successor-tests/lorexpr/lorexpr.c @@ -1,6 +1,6 @@ -void f() { - int a, b; - if (a || b) { - } - ; +void f() { + int a, b; + if (a || b) { + } + ; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/ltrbinopexpr/ltrbinopexpr.c b/cpp/ql/test/successor-tests/ltrbinopexpr/ltrbinopexpr.c index ad88c668f0fe..cf093d36531a 100644 --- a/cpp/ql/test/successor-tests/ltrbinopexpr/ltrbinopexpr.c +++ b/cpp/ql/test/successor-tests/ltrbinopexpr/ltrbinopexpr.c @@ -1,43 +1,43 @@ -void f() { - int i, j; - int* p, q; - - i + j; - i - j; - i * j; - i / j; - i % j; - - p + i; - p - i; - //q - p; - - i << j; - i >> j; - - i & j; - i | j; - i ^ j; - i == j; - i != j; - i > j; - i < j; - i >= j; - i <= j; - - i += j; - i -= j; - i *= j; - i /= j; - i %= j; - i <<= j; - i >>= j; - i &= j; - i |= j; - i ^= j; - - p += i; - p -= i; - - i, j; -} +void f() { + int i, j; + int* p, q; + + i + j; + i - j; + i * j; + i / j; + i % j; + + p + i; + p - i; + //q - p; + + i << j; + i >> j; + + i & j; + i | j; + i ^ j; + i == j; + i != j; + i > j; + i < j; + i >= j; + i <= j; + + i += j; + i -= j; + i *= j; + i /= j; + i %= j; + i <<= j; + i >>= j; + i &= j; + i |= j; + i ^= j; + + p += i; + p -= i; + + i, j; +} diff --git a/cpp/ql/test/successor-tests/newexpr/defconstructornewexpr/defconstructornewexpr.cpp b/cpp/ql/test/successor-tests/newexpr/defconstructornewexpr/defconstructornewexpr.cpp index b97b4c653832..0a51afb3b833 100644 --- a/cpp/ql/test/successor-tests/newexpr/defconstructornewexpr/defconstructornewexpr.cpp +++ b/cpp/ql/test/successor-tests/newexpr/defconstructornewexpr/defconstructornewexpr.cpp @@ -1,6 +1,6 @@ -class C { }; - -void f() { - new C; - return; +class C { }; + +void f() { + new C; + return; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/newexpr/newexpr/newexpr.cpp b/cpp/ql/test/successor-tests/newexpr/newexpr/newexpr.cpp index cf27d3fea04f..1060bfa898f1 100644 --- a/cpp/ql/test/successor-tests/newexpr/newexpr/newexpr.cpp +++ b/cpp/ql/test/successor-tests/newexpr/newexpr/newexpr.cpp @@ -1,9 +1,9 @@ -class C { - public: - C(int i, int j); -}; - -void f() { - int a, b, c, d; - new C(a + b, c - d); -} +class C { + public: + C(int i, int j); +}; + +void f() { + int a, b, c, d; + new C(a + b, c - d); +} diff --git a/cpp/ql/test/successor-tests/questionexpr/questionexpr.c b/cpp/ql/test/successor-tests/questionexpr/questionexpr.c index d77accad4433..aeb257d9a61b 100644 --- a/cpp/ql/test/successor-tests/questionexpr/questionexpr.c +++ b/cpp/ql/test/successor-tests/questionexpr/questionexpr.c @@ -1,4 +1,4 @@ -void f() { - int a, b, c, d, x; - x = a == b ? c + b : d - b; +void f() { + int a, b, c, d, x; + x = a == b ? c + b : d - b; } \ No newline at end of file diff --git a/cpp/ql/test/successor-tests/returnstmt/returnstmt.c b/cpp/ql/test/successor-tests/returnstmt/returnstmt.c index e0383f5f3367..f32c06327285 100644 --- a/cpp/ql/test/successor-tests/returnstmt/returnstmt.c +++ b/cpp/ql/test/successor-tests/returnstmt/returnstmt.c @@ -1,10 +1,10 @@ - -void return_void() { - return; - ; -} - -int return_int() { - return 1; - ; -} + +void return_void() { + return; + ; +} + +int return_int() { + return 1; + ; +} diff --git a/cpp/ql/test/successor-tests/staticlocals/staticlocals/staticlocals.cpp b/cpp/ql/test/successor-tests/staticlocals/staticlocals/staticlocals.cpp index 4420d4d05f6b..f13e2fa9583c 100644 --- a/cpp/ql/test/successor-tests/staticlocals/staticlocals/staticlocals.cpp +++ b/cpp/ql/test/successor-tests/staticlocals/staticlocals/staticlocals.cpp @@ -1,13 +1,13 @@ -int g() { - return 1; -} - -int h() { - return 1; -} - -void f() { - static int i = g(), j = h(); - static int k = g(); - ; -} +int g() { + return 1; +} + +int h() { + return 1; +} + +void f() { + static int i = g(), j = h(); + static int k = g(); + ; +} diff --git a/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/revsubscriptexpr.c b/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/revsubscriptexpr.c index 0850bf62ccfe..f68a5bad0d90 100644 --- a/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/revsubscriptexpr.c +++ b/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/revsubscriptexpr.c @@ -1,5 +1,5 @@ -void g() { - double x[5]; - int i, a, b; - i = (a + b)[x]; -} +void g() { + double x[5]; + int i, a, b; + i = (a + b)[x]; +} diff --git a/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/subscriptexpr.c b/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/subscriptexpr.c index 22bed0c1afb4..1a98c810aa7e 100644 --- a/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/subscriptexpr.c +++ b/cpp/ql/test/successor-tests/subscriptexpr/subscriptexpr/subscriptexpr.c @@ -1,5 +1,5 @@ -void f() { - double x[5]; - int i, a, b; - i = x[a + b]; -} +void f() { + double x[5]; + int i, a, b; + i = x[a + b]; +} diff --git a/cpp/ql/test/successor-tests/switchstmt/nodefaultswitchstmt/nodefaultswitchstmt.c b/cpp/ql/test/successor-tests/switchstmt/nodefaultswitchstmt/nodefaultswitchstmt.c index f12cd7d3620d..bdd18c622e66 100644 --- a/cpp/ql/test/successor-tests/switchstmt/nodefaultswitchstmt/nodefaultswitchstmt.c +++ b/cpp/ql/test/successor-tests/switchstmt/nodefaultswitchstmt/nodefaultswitchstmt.c @@ -1,7 +1,7 @@ -void f(int x) { - switch (x) { - case 1: - case 2: - } - ; -} +void f(int x) { + switch (x) { + case 1: + case 2: + } + ; +} diff --git a/cpp/ql/test/successor-tests/switchstmt/switchstmt/switchstmt.c b/cpp/ql/test/successor-tests/switchstmt/switchstmt/switchstmt.c index 4f618a21e203..32be6e43b68d 100644 --- a/cpp/ql/test/successor-tests/switchstmt/switchstmt/switchstmt.c +++ b/cpp/ql/test/successor-tests/switchstmt/switchstmt/switchstmt.c @@ -1,8 +1,8 @@ -void f(int x) { - switch (x) { - case 1: - case 2: - default: - } - ; -} +void f(int x) { + switch (x) { + case 1: + case 2: + default: + } + ; +} diff --git a/cpp/ql/test/successor-tests/unaryopexpr/unaryopexpr.c b/cpp/ql/test/successor-tests/unaryopexpr/unaryopexpr.c index 0d2ce20ab448..625be3434ee4 100644 --- a/cpp/ql/test/successor-tests/unaryopexpr/unaryopexpr.c +++ b/cpp/ql/test/successor-tests/unaryopexpr/unaryopexpr.c @@ -1,14 +1,14 @@ -void f() { - int i; - &i; - - -i; - //+i - ~i; - !i; - - i++; - i--; - ++i; - --i; -} +void f() { + int i; + &i; + + -i; + //+i + ~i; + !i; + + i++; + i--; + ++i; + --i; +} diff --git a/cpp/ql/test/successor-tests/whilestmt/whilestmt.c b/cpp/ql/test/successor-tests/whilestmt/whilestmt.c index 02af4089e760..9de843ba20a6 100644 --- a/cpp/ql/test/successor-tests/whilestmt/whilestmt.c +++ b/cpp/ql/test/successor-tests/whilestmt/whilestmt.c @@ -1,45 +1,45 @@ -void always_false_1() { - while(0) { - l1:; - } - l2:; -} - -void always_false_2() { - int done = 1; - while(!done) { - done = 0; - } -} - -void always_true_1() { - while(1) { - l1:; - break; - } - l2:; -} - -void always_true_2() { - while(1) { - l1:; - break; - l2:; - } - l3:; -} - -void always_true_3() { - while(1) { - l1:; - } - l2:; -} - -void normal() { - int i = 0; - while(i < 10) { - ++i; - } - l:; -} +void always_false_1() { + while(0) { + l1:; + } + l2:; +} + +void always_false_2() { + int done = 1; + while(!done) { + done = 0; + } +} + +void always_true_1() { + while(1) { + l1:; + break; + } + l2:; +} + +void always_true_2() { + while(1) { + l1:; + break; + l2:; + } + l3:; +} + +void always_true_3() { + while(1) { + l1:; + } + l2:; +} + +void normal() { + int i = 0; + while(i < 10) { + ++i; + } + l:; +} From 26abf5d4a2a851b3a6b82e6e1f387dacaf80112a Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 20 Sep 2018 11:53:27 -0700 Subject: [PATCH 094/110] Force LF for basically everything. --- .editorconfig | 2 +- .gitattributes | 13 + change-notes/1.19/analysis-cpp.md | 40 +- cpp/ql/src/plugin.xml | 32 +- cpp/ql/src/tools/instantiate_templates.py | 274 +++--- csharp/ql/src/plugin.xml | 32 +- .../standalone/controlflow/ControlFlow.cs | 28 +- docs/ql-style-guide.md | 834 +++++++++--------- .../MissingParentBean.xml | 54 +- .../TooManyBeans.xml | 16 +- .../Refactoring Opportunities/UnusedBean.xml | 12 +- .../UselessPropertyOverride.xml | 46 +- .../AvoidAutowiring.xml | 24 +- .../DontUseConstructorArgIndex.xml | 24 +- .../ImportsFirst.xml | 52 +- .../NoBeanDescription.xml | 52 +- .../UseIdInsteadOfName.xml | 30 +- .../UseLocalRef.xml | 32 +- .../UseSetterInjection.java | 42 +- .../UseShortcutForms.xml | 76 +- .../MissingSetters.java | 18 +- .../MissingSetters.xml | 14 +- .../Arithmetic/BadAbsOfRandom.java | 34 +- .../Arithmetic/RandomUsedOnce.java | 22 +- .../Finalization/NullifiedSuperFinalize.java | 58 +- .../Frameworks/JUnit/BadSuiteMethod.java | 40 +- .../Frameworks/JUnit/TearDownNoSuper.java | 128 +-- .../Frameworks/JUnit/TestCaseNoTests.java | 54 +- .../I18N/MissingLocaleArgument.java | 26 +- .../IncorrectSerialVersionUID.java | 20 +- .../IncorrectSerializableMethods.java | 48 +- ...issingVoidConstructorOnExternalizable.java | 72 +- ...MissingVoidConstructorsOnSerializable.java | 82 +- .../NonSerializableComparator.java | 30 +- .../Serialization/NonSerializableField.java | 54 +- .../NonSerializableFieldTooGeneral.java | 58 +- .../NonSerializableInnerClass.java | 64 +- .../Serialization/ReadResolveObject.java | 78 +- .../TransientNotSerializable.java | 22 +- .../Dead Code/FinalizerNullsFields.java | 26 +- .../DroppedExceptions-comment.java | 16 +- .../DroppedExceptions-good.java | 18 +- .../DroppedExceptions-ignore.java | 8 +- .../CallsToRunFinalizersOnExit.java | 14 +- .../CallsToStringToString.java | 16 +- .../Undesirable Calls/DefaultToString.java | 40 +- .../Undesirable Calls/GarbageCollection.java | 28 +- .../Undesirable Calls/PrintLnArray.java | 46 +- java/ql/src/plugin.xml | 32 +- .../library-tests/comments/.gitattributes | 1 + .../library-tests/comments/TestWindows.java | 44 +- .../CloseReaderTest/CloseReaderTest.java | 50 +- .../LoopVarReadTest/LoopVarReadTest.java | 32 +- .../successors/SaveFileTest/SaveFileTest.java | 112 +-- .../successors/TestBreak/TestBreak.java | 172 ++-- .../successors/TestContinue/TestContinue.java | 118 +-- .../successors/TestFinally/TestFinally.java | 300 +++---- .../TestFinallyBreakContinue.java | 216 ++--- .../TestLoopBranch/TestLoopBranch.java | 238 ++--- .../successors/TestThrow/TestThrow.java | 270 +++--- .../successors/TestTryCatch/TestTryCatch.java | 88 +- .../AlertSuppression/.gitattributes | 1 + .../AlertSuppression/TestWindows.java | 56 +- javascript/ql/src/plugin.xml | 32 +- 64 files changed, 2313 insertions(+), 2298 deletions(-) create mode 100644 java/ql/test/library-tests/comments/.gitattributes create mode 100644 java/ql/test/query-tests/AlertSuppression/.gitattributes diff --git a/.editorconfig b/.editorconfig index 413e18ca6589..268bd5a7ecbc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp,html,js,mjs,ts,json,yml,c,cpp,h,hpp}] +[*] end_of_line = lf diff --git a/.gitattributes b/.gitattributes index d945c85a0158..bd6bfa96bcc8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,12 +16,25 @@ *.dbscheme eol=lf *.qhelp eol=lf *.html eol=lf +*.htm eol=lf +*.xhtml eol=lf +*.xhtm eol=lf *.js eol=lf *.mjs eol=lf *.ts eol=lf *.json eol=lf *.yml eol=lf +*.yaml eol=lf *.c eol=lf *.cpp eol=lf *.h eol=lf *.hpp eol=lf +*.md eol=lf +*.stats eol=lf +*.xml eol=lf +*.sh eol=lf +*.pl eol=lf +*.java eol=lf +*.cs eol=lf +*.py eol=lf +*.lua eol=lf diff --git a/change-notes/1.19/analysis-cpp.md b/change-notes/1.19/analysis-cpp.md index 7fcf1fd234c0..5a3340603c87 100644 --- a/change-notes/1.19/analysis-cpp.md +++ b/change-notes/1.19/analysis-cpp.md @@ -1,20 +1,20 @@ -# Improvements to C/C++ analysis - -## General improvements - -## New queries - -| **Query** | **Tags** | **Purpose** | -|-----------------------------|-----------|--------------------------------------------------------------------| -| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* | - -## Changes to existing queries - -| **Query** | **Expected impact** | **Change** | -|----------------------------|------------------------|------------------------------------------------------------------| -| Resource not released in destructor | Fewer false positive results | Placement new is now excluded from the query. | - - -## Changes to QL libraries - -* Added a hash consing library for structural comparison of expressions. +# Improvements to C/C++ analysis + +## General improvements + +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* | + +## Changes to existing queries + +| **Query** | **Expected impact** | **Change** | +|----------------------------|------------------------|------------------------------------------------------------------| +| Resource not released in destructor | Fewer false positive results | Placement new is now excluded from the query. | + + +## Changes to QL libraries + +* Added a hash consing library for structural comparison of expressions. diff --git a/cpp/ql/src/plugin.xml b/cpp/ql/src/plugin.xml index e08a29947a6c..d7036c7c3479 100644 --- a/cpp/ql/src/plugin.xml +++ b/cpp/ql/src/plugin.xml @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/cpp/ql/src/tools/instantiate_templates.py b/cpp/ql/src/tools/instantiate_templates.py index 0beeeed20d8e..d549a85f82d3 100644 --- a/cpp/ql/src/tools/instantiate_templates.py +++ b/cpp/ql/src/tools/instantiate_templates.py @@ -1,137 +1,137 @@ -import sys -import os.path -import glob -import re -import json - -BEGIN_TEMPLATE = re.compile(r"^/\*template\s*$") -END_TEMPLATE = re.compile(r"^\*/\s*$") - -def expand_template_params(args, param_arg_map): - '''Given a list of template arguments that may reference template parameters - of the current template, return a new list of template arguments with each - parameter use replaced with the appropriate fully-qualified argument for - that parameter.''' - result = [] - for arg in args: - if arg in param_arg_map: - result.append(param_arg_map[arg]) - else: - result.append(arg) - - return result - -def find_instantiation(module, args, templates): - '''Given a template module and a set of template arguments, find the module - name of the instantiation of that module with those arguments.''' - template = templates[module] - for instantiation in template["template_def"]["instantiations"]: - if instantiation["args"] == args: - return instantiation["name"] - return None - -def instantiate_template(template, instantiation, root, templates): - '''Create a single instantiation of a template.''' - template_def = template["template_def"] - output_components = instantiation["name"].split(".") - output_path = root - for component in output_components: - output_path = os.path.join(output_path, component) - output_path = output_path + ".qll" - with open(output_path, "w") as output: - output.write( -""" -/* - * THIS FILE IS AUTOMATICALLY GENERATED FROM '%s'. - * DO NOT EDIT MANUALLY. - */ - -""" % (template["name"].replace(".", "/") + ".qllt") - ) - param_arg_map = {} - for param_index in range(len(template_def["params"])): - param = template_def["params"][param_index] - arg = instantiation["args"][param_index] - output.write("private import %s as %s // Template parameter\n" % (arg, param)) - param_arg_map[param] = arg - for import_record in template_def["imports"]: - if "access" in import_record: - output.write(import_record["access"] + " ") - imported_module = find_instantiation(import_record["module"], - expand_template_params(import_record["args"], param_arg_map), templates) - output.write("import %s // %s<%s>\n" % - ( - imported_module, - import_record["module"], - ", ".join(import_record["args"]) - ) - ) - - output.writelines(template_def["body_lines"]) - -def generate_instantiations(template, root, templates): - '''Create a .qll source file for each instantiation of the specified template.''' - template_def = template["template_def"] - if "instantiations" in template_def: - for instantiation in template_def["instantiations"]: - instantiate_template(template, instantiation, root, templates) - -def read_template(template_path, module_name): - '''Read a .qllt template file from template_path, using module_name as the - fully qualified name of the module.''' - with open(template_path) as input: - in_template = False - template_text = "" - template_def = None - body_lines = [] - for line in iter(input): - if in_template: - if END_TEMPLATE.match(line): - template_def = json.loads(template_text) - in_template = False - else: - template_text += line - else: - if BEGIN_TEMPLATE.match(line) and not template_def: - in_template = True - else: - body_lines.append(line) - - if template_def: - template_def["body_lines"] = body_lines - - result = { "name": module_name } - if template_def: - result["template_def"] = template_def - return result - -def module_name_from_path_impl(path): - (head, tail) = os.path.split(path) - if head == "": - return tail - else: - return module_name_from_path(head) + "." + tail - -def module_name_from_path(path): - '''Compute the fully qualified name of a module from the path of its .qll[t] - file. The path should be relative to the library root.''' - (module_root, ext) = os.path.splitext(path) - return module_name_from_path_impl(module_root) - -def main(): - templates = {} - - root = sys.argv[1] - for template_path in glob.glob(os.path.join(root, "**\\*.qllt"), recursive = True): - print(template_path) - module_name = module_name_from_path(os.path.relpath(template_path, root)) - print(module_name) - template = read_template(template_path, module_name) - templates[template["name"]] = template - - for name, template in templates.items(): - if "template_def" in template: - generate_instantiations(template, root, templates) - -if __name__ == "__main__": - main() +import sys +import os.path +import glob +import re +import json + +BEGIN_TEMPLATE = re.compile(r"^/\*template\s*$") +END_TEMPLATE = re.compile(r"^\*/\s*$") + +def expand_template_params(args, param_arg_map): + '''Given a list of template arguments that may reference template parameters + of the current template, return a new list of template arguments with each + parameter use replaced with the appropriate fully-qualified argument for + that parameter.''' + result = [] + for arg in args: + if arg in param_arg_map: + result.append(param_arg_map[arg]) + else: + result.append(arg) + + return result + +def find_instantiation(module, args, templates): + '''Given a template module and a set of template arguments, find the module + name of the instantiation of that module with those arguments.''' + template = templates[module] + for instantiation in template["template_def"]["instantiations"]: + if instantiation["args"] == args: + return instantiation["name"] + return None + +def instantiate_template(template, instantiation, root, templates): + '''Create a single instantiation of a template.''' + template_def = template["template_def"] + output_components = instantiation["name"].split(".") + output_path = root + for component in output_components: + output_path = os.path.join(output_path, component) + output_path = output_path + ".qll" + with open(output_path, "w") as output: + output.write( +""" +/* + * THIS FILE IS AUTOMATICALLY GENERATED FROM '%s'. + * DO NOT EDIT MANUALLY. + */ + +""" % (template["name"].replace(".", "/") + ".qllt") + ) + param_arg_map = {} + for param_index in range(len(template_def["params"])): + param = template_def["params"][param_index] + arg = instantiation["args"][param_index] + output.write("private import %s as %s // Template parameter\n" % (arg, param)) + param_arg_map[param] = arg + for import_record in template_def["imports"]: + if "access" in import_record: + output.write(import_record["access"] + " ") + imported_module = find_instantiation(import_record["module"], + expand_template_params(import_record["args"], param_arg_map), templates) + output.write("import %s // %s<%s>\n" % + ( + imported_module, + import_record["module"], + ", ".join(import_record["args"]) + ) + ) + + output.writelines(template_def["body_lines"]) + +def generate_instantiations(template, root, templates): + '''Create a .qll source file for each instantiation of the specified template.''' + template_def = template["template_def"] + if "instantiations" in template_def: + for instantiation in template_def["instantiations"]: + instantiate_template(template, instantiation, root, templates) + +def read_template(template_path, module_name): + '''Read a .qllt template file from template_path, using module_name as the + fully qualified name of the module.''' + with open(template_path) as input: + in_template = False + template_text = "" + template_def = None + body_lines = [] + for line in iter(input): + if in_template: + if END_TEMPLATE.match(line): + template_def = json.loads(template_text) + in_template = False + else: + template_text += line + else: + if BEGIN_TEMPLATE.match(line) and not template_def: + in_template = True + else: + body_lines.append(line) + + if template_def: + template_def["body_lines"] = body_lines + + result = { "name": module_name } + if template_def: + result["template_def"] = template_def + return result + +def module_name_from_path_impl(path): + (head, tail) = os.path.split(path) + if head == "": + return tail + else: + return module_name_from_path(head) + "." + tail + +def module_name_from_path(path): + '''Compute the fully qualified name of a module from the path of its .qll[t] + file. The path should be relative to the library root.''' + (module_root, ext) = os.path.splitext(path) + return module_name_from_path_impl(module_root) + +def main(): + templates = {} + + root = sys.argv[1] + for template_path in glob.glob(os.path.join(root, "**\\*.qllt"), recursive = True): + print(template_path) + module_name = module_name_from_path(os.path.relpath(template_path, root)) + print(module_name) + template = read_template(template_path, module_name) + templates[template["name"]] = template + + for name, template in templates.items(): + if "template_def" in template: + generate_instantiations(template, root, templates) + +if __name__ == "__main__": + main() diff --git a/csharp/ql/src/plugin.xml b/csharp/ql/src/plugin.xml index f44185688ff6..532f6c121ad7 100644 --- a/csharp/ql/src/plugin.xml +++ b/csharp/ql/src/plugin.xml @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/csharp/ql/test/library-tests/standalone/controlflow/ControlFlow.cs b/csharp/ql/test/library-tests/standalone/controlflow/ControlFlow.cs index 56a6e5121cbd..1dedc955c334 100644 --- a/csharp/ql/test/library-tests/standalone/controlflow/ControlFlow.cs +++ b/csharp/ql/test/library-tests/standalone/controlflow/ControlFlow.cs @@ -1,14 +1,14 @@ -// semmle-extractor-options: --standalone - -using System; - -class Cfg -{ - void F() - { - var v = new InvalidType(); - Debug.Assert(v.a.b, "This is true"); - - new CounterCreationData() { CounterHelp = string.Empty, CounterType = v.Type }; - } -} +// semmle-extractor-options: --standalone + +using System; + +class Cfg +{ + void F() + { + var v = new InvalidType(); + Debug.Assert(v.a.b, "This is true"); + + new CounterCreationData() { CounterHelp = string.Empty, CounterType = v.Type }; + } +} diff --git a/docs/ql-style-guide.md b/docs/ql-style-guide.md index ad30770944dd..2e2f8eb39f7b 100644 --- a/docs/ql-style-guide.md +++ b/docs/ql-style-guide.md @@ -1,417 +1,417 @@ -# QL Style Guide - -## Introduction - -This document describes how to format the QL code you contribute to this repository. It covers aspects such as layout, white-space, naming and documentation. Adhering to consistent standards makes code easier to read and maintain. Of course, these are only guidelines, and can be overridden as the need arises on a case-by-case basis. Where existing code deviates from these guidelines, prefer consistency with the surrounding code. - -Words in *italic* are defined in the [Glossary](#glossary). - -## Indentation -1. *Always* use 2 spaces for indentation. -1. *Always* indent: - - The *body* of a module, newtype, class or predicate - - The second and subsequent lines after you use a line break to split a long line - - The *body* of a `from`, `where` or `select` clause where it spans multiple lines - - The *body* of a *quantifier* that spans multiple lines - - -### Examples - -```ql -module Helpers { - /** ... */ - class X ... { - /** ... */ - int getNumberOfChildren () { - result = count(int child | - exists(this.getChild(child)) - ) - } - } -} -``` - -```ql -from Call c, string reason -where isDeprecated(c, reason) -select c, "This call to '$@' is deprecated because " + reason + ".", - c.getTarget(), c.getTarget().getName() -``` - -## Line breaks -1. Use UNIX line endings. -1. Lines *must not* exceed 100 characters. -1. Long lines *should* be split with a line break, and the following lines *must* be indented one level until the next "regular" line break. -1. There *should* be a single blank line: - - Between the file documentation and the first `import` - - Before each declaration, except for the first declaration in a *body* - - Before the `from`-`where`-`select` section in a query file -1. *Avoid* two or more adjacent blank lines. -1. There *must* be a new line after the *annotations* `cached`, `pragma`, `language` and `bindingset`. Other *annotations* do not have a new line. -1. There *should not* be additional blank lines within a predicate. -1. There *may* be a new line: - - Immediately after the `from`, `where` or `select` keywords in a query. - - Immediately after `if`, `then`, or `else` keywords. The `then` and `else` parts *should* be consistent. -1. *Avoid* other line breaks in declarations, other than to break long lines. -1. When operands of *binary operators* span two lines, the operator *should* be placed at the end of the first line. - -### Examples - -```ql -cached -private int getNumberOfParameters() { - ... -} -``` - -```ql -predicate methodStats(string qualifiedName, string name, - int numberOfParameters, int numberOfStatements, int numberOfExpressions, - int linesOfCode, int nestingDepth, int numberOfBranches) { - ... -} -``` - -```ql -from Method main -where main.getName() = "Main" -select main, "This is the program entry point." -``` - -```ql -from Method main -where - main.getName() = "Main" and - main.getNumberOfParameters() = 0 -select main, "Main method has no parameters." -``` - -```ql - if x.isPublic() - then result = "public" - else result = "private" -``` - -```ql - if x.isPublic() then - result = "public" - else - result = "private" -``` - -```ql - if - x.isPublic() - then - result = "public" - else - result = "private" -``` - -## Braces -1. Braces follow [Stroustrup](https://en.wikipedia.org/wiki/Indentation_style#Variant:_Stroustrup) style. The opening `{` *must* be placed at the end of the preceding line. -1. The closing `}` *must* be placed on its own line, indented to the outer level, or be on the same line as the opening `{`. -1. Braces of empty blocks *may* be placed on a single line, with a single space separating the braces. -1. Short predicates, not exceeding the maximum line width, *may* be placed on a single line, with a space following the opening brace and preceding the closing brace. - -### Examples - -```ql -class ThrowException extends ThrowExpr { - Foo() { - this.getTarget() instanceof ExceptionClass - } - - override string toString() { result = "Throw Exception" } -} -``` - -## Spaces -1. There *must* be a space or line break: - - Surrounding each `=` and `|` - - After each `,` -1. There *should* be a space or line break: - - Surrounding each *binary operator*, which *must* be balanced - - Surrounding `..` in a range - - Exceptions to this may be made to save space or to improve readability. -1. *Avoid* other spaces, for example: - - After a *quantifier/aggregation* keyword - - After the predicate name in a *call* - - Inside brackets used for *calls*, single-line quantifiers, and parenthesised formulas - - Surrounding a `.` - - Inside the opening or closing `[ ]` in a range expression - - Inside casts `a.(X)` -1. *Avoid* multiple spaces, except for indentation, and *avoid* additional indentation to align formulas, parameters or arguments. -1. *Do not* put whitespace on blank lines, or trailing on the end of a line. -1. *Do not* use tabs. - - -### Examples - -```ql -cached -private predicate foo(Expr e, Expr p) { - exists(int n | - n in [0 .. 1] | - e = p.getChild(n + 1) - ) -} -``` - -## Naming -1. Use [PascalCase](http://wiki.c2.com/?PascalCase) for: - - `class` names - - `module` names - - `newtype` names -1. Use [camelCase](https://en.wikipedia.org/wiki/Camel_case) for: - - Predicate names - - Variable names -1. Newtype predicate names *should* begin with `T`. -1. Predicates that have a result *should* be named `get...` -1. Predicates that can return multiple results *should* be named `getA...` or `getAn...` -1. Predicates that don't have a result or parameters *should* be named `is...` or `has...` -1. *Avoid* underscores in names. -1. *Avoid* short or single-letter names for classes, predicates and fields. -1. Short or single letter names for parameters and *quantifiers* *may* be used provided that they are sufficiently clear. -1. Use names as they are used in the target-language specification. -1. Use American English. - -### Examples - -```ql -/** ... */ -predicate calls(Callable caller, Callable callee) { - ... -} -``` - -```ql -/** ... */ -class Type extends ... { - /** ... */ - string getName() { ... } - - /** ... */ - predicate declares(Member m) { ... } - - /** ... */ - predicate isGeneric() { ... } - - /** ... */ - Type getTypeParameter(int n) { ... } - - /** ... */ - Type getATypeParameter() { ... } -} -``` - -## Documentation - -General requirements: - -1. Documentation *must* adhere to the [QLDoc specification](https://help.semmle.com/QL/QLDocSpecification.html). -1. Use `/** ... */` for documentation, even for single line comments. -1. For single-line documentation, the `/**` and `*/` are written on the same line as the comment. -1. For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. -1. Use full sentences, with capital letters and full stops. -1. Use American English. -1. Documentation comments *should* be appropriate for users of the code. -1. Documentation for maintainers of the code *must* use normal comments. - -Documentation for specific items: - -1. Public declarations *must* be documented. -1. Non-public declarations *should* be documented. -1. Declarations in query files *should* be documented. -1. Library files (`.qll` files) *should* be have a documentation comment at the top of the file. -1. Query files, except for tests, *must* have a QLDoc query documentation comment at the top of the file. -1. Predicates that do not have a result *should* be documented `/** Holds if ... */` -1. Predicates that have a result *should* be documented `/** Gets ... */` -1. All predicate parameters *should* be referred to in the predicate documentation. -1. Reference names, such as types and parameters, using backticks `` ` ``. -1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. -1. Classes *should* be documented in the singular, for example `/* An expression. */` -1. Where a class denotes a generic concept with subclasses, list those subclasses. -1. Declarations that are deprecated *should* be documented as `DEPRECATED: ...` -1. Declarations that are for internal use *should* be documented as `INTERNAL: Do not use`. - -### Examples - -```ql -/** Provides logic for determining constant expressions. */ -``` - -```ql -/** - * Holds if the qualifier of this call has type `qualifierType`. - * `isExactType` indicates whether the type is exact, that is, whether - * the qualifier is guaranteed not to be a subtype of `qualifierType`. - */ -``` -```ql -/** - * A delegate declaration, for example - * ``` - * delegate void Logger(string text); - * ``` - */ -class Delegate extends ... -``` - -```ql -/** - * An element that can be called. - * - * Either a method (`Method`), a constructor (`Constructor`), a destructor - * (`Destructor`), an operator (`Operator`), an accessor (`Accessor`), - * an anonymous function (`AnonymousFunctionExpr`), or a local function - * (`LocalFunction`). - */ -class Callable extends ... -``` - -```ql -/** DEPRECATED: Use `getAnExpr()` instead. */ -deprecated Expr getInitializer() -``` - -```ql -/** - * INTERNAL: Do not use. - */ -``` - -## Formulas -1. *Prefer* one *conjunct* per line. -1. Write the `and` at the end of the line. This also applies in `where` clauses. -1. *Prefer* to write the `or` keyword on its own line. -1. The `or` keyword *may* be written at the end of a line, or within a line, provided that it has no unparenthesised `and` operands. -1. Single-line formulas *may* be used in order to save space or add clarity, particularly in the *body* of a *quantifier/aggregation*. -1. *Always* use brackets to clarify the precedence of: - - `implies` - - `if`-`then`-`else` -1. Parenthesised formulas *can* be written: - - Within a single line. There *should not* be an additional space following the opening parenthesis or preceding the closing parenthesis. - - Spanning multiple lines. The opening parenthesis *should* be placed at the end of the preceding line, the body should be indented one level, and the closing bracket should be placed on a new line at the outer indentation. -1. *Quantifiers/aggregations* *can* be written: - - Within a single line. In this case, there is no space to the inside of the parentheses, or after the quantifier keyword. - - Across multiple lines. In this case, type declarations are on the same line as the quantifier, the `|` *may* be at the end of the line, or *may* be on its own line, and the body of the quantifier *must* be indented one level. The closing `)` is written on a new line, at the outer indentation. -1. `if`-`then`-`else` *can* be written: - - On a single line - - With the *body* after the `if`/`then`/`else` keyword - - With the *body* indented on the next line - - *Always* parenthesise the `else` part if it is a compound formula. -1. The `and` and `else` keywords *may* be placed on the same line as the closing parenthesis. -1. The `and` and `else` keywords *may* be "cuddled": `) else (` -1. *Always* qualify *calls* to predicates of the same class with `this`. -2. *Prefer* postfix casts `a.(Expr)` to prefix casts `(Expr)a`. - -### Examples - -```ql - argumentType.isImplicitlyConvertibleTo(parameterType) - or - argumentType instanceof NullType and - result.getParameter(i).isOut() and - parameterType instanceof SimpleType - or - reflectionOrDynamicArg(argumentType, parameterType) -``` - -```ql - this.getName() = "Finalize" and not exists(this.getAParameter()) -``` - -```ql - e1.getType() instanceof BoolType and ( - b1 = true - or - b1 = false - ) and ( - b2 = true - or - b2 = false - ) -``` - -```ql - if e1 instanceof BitwiseOrExpr or e1 instanceof LogicalOrExpr then ( - impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and - b1 = false - ) else ( - e1.getType() instanceof BoolType and - e1 = e2 and - b1 = b2 and - (b1 = true or b1 = false) - ) -``` - -```ql - (x instanceof Exception implies x.isPublic()) and y instanceof Exception -``` - -```ql - x instanceof Exception implies (x.isPublic() and y instanceof Exception) -``` - -```ql - exists(Type arg | arg = this.getAChild() | arg instanceof TypeParameter) -``` - -```ql - exists(Type qualifierType | - this.hasNonExactQualifierType(qualifierType) | - result = getANonExactQualifierSubType(qualifierType) - ) -``` - -```ql - methods = count(Method m | t = m.getDeclaringType() and not ilc(m)) -``` - -```ql - if n = 0 then result = 1 else result = n * f(n - 1) -``` - -```ql - if n = 0 - then result = 1 - else result = n * f(n - 1) -``` - -```ql - if - n = 0 - then - result = 1 - else - result = n * f(n - 1) -``` - -```ql - if exists(this.getContainingType()) then ( - result = "A nested class" and - parentName = this.getContainingType().getFullyQualifiedName() - ) else ( - result = parentName + "." + this.getName() and - parentName = this.getNamespace().getFullyQualifiedName() - ) -``` - -## Glossary - -| Phrase | Meaning | -|-------------|----------| -| *[annotation](https://help.semmle.com/QL/QLLanguageSpecification.html#annotations)* | An additional specifier used to modify a declaration, such as `private`, `override`, `deprecated`, `pragma`, `bindingset`, or `cached`. | -| *body* | The text inside `{ }`, `( )`, or each section of an `if`-`then`-`else` or `from`-`where`-`select`. | -| *binary operator* | An operator with two operands, such as comparison operators, `and`, `or`, `implies`, or arithmetic operators. | -| *call* | A *formula* that invokes a predicate, e.g. `this.isStatic()` or `calls(a,b)`. | -| *[conjunct](https://help.semmle.com/QL/QLLanguageSpecification.html#conjunctions)* | A formula that is an operand to an `and`. | -| *declaration* | A class, module, predicate, field or newtype. | -| *[disjunct](https://help.semmle.com/QL/QLLanguageSpecification.html#disjunctions)* | A formula that is an operand to an `or`. | -| *[formula](https://help.semmle.com/QL/QLLanguageSpecification.html#formulas)* | A logical expression, such as `A = B`, a *call*, a *quantifier*, `and`, `or`, `not`, `in` or `instanceof`. | -| *should/should not/avoid/prefer* | Adhere to this rule wherever possible, where it makes sense. | -| *may/can* | This is a reasonable alternative, to be used with discretion. | -| *must/always/do not* | Always adhere to this rule. | -| *[quantifier/aggregation](https://help.semmle.com/QL/QLLanguageSpecification.html#aggregations)* | `exists`, `count`, `strictcount`, `any`, `forall`, `forex` and so on. | -| *variable* | A parameter to a predicate, a field, a from variable, or a variable introduced by a *quantifier* or *aggregation*. | +# QL Style Guide + +## Introduction + +This document describes how to format the QL code you contribute to this repository. It covers aspects such as layout, white-space, naming and documentation. Adhering to consistent standards makes code easier to read and maintain. Of course, these are only guidelines, and can be overridden as the need arises on a case-by-case basis. Where existing code deviates from these guidelines, prefer consistency with the surrounding code. + +Words in *italic* are defined in the [Glossary](#glossary). + +## Indentation +1. *Always* use 2 spaces for indentation. +1. *Always* indent: + - The *body* of a module, newtype, class or predicate + - The second and subsequent lines after you use a line break to split a long line + - The *body* of a `from`, `where` or `select` clause where it spans multiple lines + - The *body* of a *quantifier* that spans multiple lines + + +### Examples + +```ql +module Helpers { + /** ... */ + class X ... { + /** ... */ + int getNumberOfChildren () { + result = count(int child | + exists(this.getChild(child)) + ) + } + } +} +``` + +```ql +from Call c, string reason +where isDeprecated(c, reason) +select c, "This call to '$@' is deprecated because " + reason + ".", + c.getTarget(), c.getTarget().getName() +``` + +## Line breaks +1. Use UNIX line endings. +1. Lines *must not* exceed 100 characters. +1. Long lines *should* be split with a line break, and the following lines *must* be indented one level until the next "regular" line break. +1. There *should* be a single blank line: + - Between the file documentation and the first `import` + - Before each declaration, except for the first declaration in a *body* + - Before the `from`-`where`-`select` section in a query file +1. *Avoid* two or more adjacent blank lines. +1. There *must* be a new line after the *annotations* `cached`, `pragma`, `language` and `bindingset`. Other *annotations* do not have a new line. +1. There *should not* be additional blank lines within a predicate. +1. There *may* be a new line: + - Immediately after the `from`, `where` or `select` keywords in a query. + - Immediately after `if`, `then`, or `else` keywords. The `then` and `else` parts *should* be consistent. +1. *Avoid* other line breaks in declarations, other than to break long lines. +1. When operands of *binary operators* span two lines, the operator *should* be placed at the end of the first line. + +### Examples + +```ql +cached +private int getNumberOfParameters() { + ... +} +``` + +```ql +predicate methodStats(string qualifiedName, string name, + int numberOfParameters, int numberOfStatements, int numberOfExpressions, + int linesOfCode, int nestingDepth, int numberOfBranches) { + ... +} +``` + +```ql +from Method main +where main.getName() = "Main" +select main, "This is the program entry point." +``` + +```ql +from Method main +where + main.getName() = "Main" and + main.getNumberOfParameters() = 0 +select main, "Main method has no parameters." +``` + +```ql + if x.isPublic() + then result = "public" + else result = "private" +``` + +```ql + if x.isPublic() then + result = "public" + else + result = "private" +``` + +```ql + if + x.isPublic() + then + result = "public" + else + result = "private" +``` + +## Braces +1. Braces follow [Stroustrup](https://en.wikipedia.org/wiki/Indentation_style#Variant:_Stroustrup) style. The opening `{` *must* be placed at the end of the preceding line. +1. The closing `}` *must* be placed on its own line, indented to the outer level, or be on the same line as the opening `{`. +1. Braces of empty blocks *may* be placed on a single line, with a single space separating the braces. +1. Short predicates, not exceeding the maximum line width, *may* be placed on a single line, with a space following the opening brace and preceding the closing brace. + +### Examples + +```ql +class ThrowException extends ThrowExpr { + Foo() { + this.getTarget() instanceof ExceptionClass + } + + override string toString() { result = "Throw Exception" } +} +``` + +## Spaces +1. There *must* be a space or line break: + - Surrounding each `=` and `|` + - After each `,` +1. There *should* be a space or line break: + - Surrounding each *binary operator*, which *must* be balanced + - Surrounding `..` in a range + - Exceptions to this may be made to save space or to improve readability. +1. *Avoid* other spaces, for example: + - After a *quantifier/aggregation* keyword + - After the predicate name in a *call* + - Inside brackets used for *calls*, single-line quantifiers, and parenthesised formulas + - Surrounding a `.` + - Inside the opening or closing `[ ]` in a range expression + - Inside casts `a.(X)` +1. *Avoid* multiple spaces, except for indentation, and *avoid* additional indentation to align formulas, parameters or arguments. +1. *Do not* put whitespace on blank lines, or trailing on the end of a line. +1. *Do not* use tabs. + + +### Examples + +```ql +cached +private predicate foo(Expr e, Expr p) { + exists(int n | + n in [0 .. 1] | + e = p.getChild(n + 1) + ) +} +``` + +## Naming +1. Use [PascalCase](http://wiki.c2.com/?PascalCase) for: + - `class` names + - `module` names + - `newtype` names +1. Use [camelCase](https://en.wikipedia.org/wiki/Camel_case) for: + - Predicate names + - Variable names +1. Newtype predicate names *should* begin with `T`. +1. Predicates that have a result *should* be named `get...` +1. Predicates that can return multiple results *should* be named `getA...` or `getAn...` +1. Predicates that don't have a result or parameters *should* be named `is...` or `has...` +1. *Avoid* underscores in names. +1. *Avoid* short or single-letter names for classes, predicates and fields. +1. Short or single letter names for parameters and *quantifiers* *may* be used provided that they are sufficiently clear. +1. Use names as they are used in the target-language specification. +1. Use American English. + +### Examples + +```ql +/** ... */ +predicate calls(Callable caller, Callable callee) { + ... +} +``` + +```ql +/** ... */ +class Type extends ... { + /** ... */ + string getName() { ... } + + /** ... */ + predicate declares(Member m) { ... } + + /** ... */ + predicate isGeneric() { ... } + + /** ... */ + Type getTypeParameter(int n) { ... } + + /** ... */ + Type getATypeParameter() { ... } +} +``` + +## Documentation + +General requirements: + +1. Documentation *must* adhere to the [QLDoc specification](https://help.semmle.com/QL/QLDocSpecification.html). +1. Use `/** ... */` for documentation, even for single line comments. +1. For single-line documentation, the `/**` and `*/` are written on the same line as the comment. +1. For multi-line documentation, the `/**` and `*/` are written on separate lines. There is a `*` preceding each comment line, aligned on the first `*`. +1. Use full sentences, with capital letters and full stops. +1. Use American English. +1. Documentation comments *should* be appropriate for users of the code. +1. Documentation for maintainers of the code *must* use normal comments. + +Documentation for specific items: + +1. Public declarations *must* be documented. +1. Non-public declarations *should* be documented. +1. Declarations in query files *should* be documented. +1. Library files (`.qll` files) *should* be have a documentation comment at the top of the file. +1. Query files, except for tests, *must* have a QLDoc query documentation comment at the top of the file. +1. Predicates that do not have a result *should* be documented `/** Holds if ... */` +1. Predicates that have a result *should* be documented `/** Gets ... */` +1. All predicate parameters *should* be referred to in the predicate documentation. +1. Reference names, such as types and parameters, using backticks `` ` ``. +1. Give examples of code in the target language, enclosed in ```` ``` ```` or `` ` ``. +1. Classes *should* be documented in the singular, for example `/* An expression. */` +1. Where a class denotes a generic concept with subclasses, list those subclasses. +1. Declarations that are deprecated *should* be documented as `DEPRECATED: ...` +1. Declarations that are for internal use *should* be documented as `INTERNAL: Do not use`. + +### Examples + +```ql +/** Provides logic for determining constant expressions. */ +``` + +```ql +/** + * Holds if the qualifier of this call has type `qualifierType`. + * `isExactType` indicates whether the type is exact, that is, whether + * the qualifier is guaranteed not to be a subtype of `qualifierType`. + */ +``` +```ql +/** + * A delegate declaration, for example + * ``` + * delegate void Logger(string text); + * ``` + */ +class Delegate extends ... +``` + +```ql +/** + * An element that can be called. + * + * Either a method (`Method`), a constructor (`Constructor`), a destructor + * (`Destructor`), an operator (`Operator`), an accessor (`Accessor`), + * an anonymous function (`AnonymousFunctionExpr`), or a local function + * (`LocalFunction`). + */ +class Callable extends ... +``` + +```ql +/** DEPRECATED: Use `getAnExpr()` instead. */ +deprecated Expr getInitializer() +``` + +```ql +/** + * INTERNAL: Do not use. + */ +``` + +## Formulas +1. *Prefer* one *conjunct* per line. +1. Write the `and` at the end of the line. This also applies in `where` clauses. +1. *Prefer* to write the `or` keyword on its own line. +1. The `or` keyword *may* be written at the end of a line, or within a line, provided that it has no unparenthesised `and` operands. +1. Single-line formulas *may* be used in order to save space or add clarity, particularly in the *body* of a *quantifier/aggregation*. +1. *Always* use brackets to clarify the precedence of: + - `implies` + - `if`-`then`-`else` +1. Parenthesised formulas *can* be written: + - Within a single line. There *should not* be an additional space following the opening parenthesis or preceding the closing parenthesis. + - Spanning multiple lines. The opening parenthesis *should* be placed at the end of the preceding line, the body should be indented one level, and the closing bracket should be placed on a new line at the outer indentation. +1. *Quantifiers/aggregations* *can* be written: + - Within a single line. In this case, there is no space to the inside of the parentheses, or after the quantifier keyword. + - Across multiple lines. In this case, type declarations are on the same line as the quantifier, the `|` *may* be at the end of the line, or *may* be on its own line, and the body of the quantifier *must* be indented one level. The closing `)` is written on a new line, at the outer indentation. +1. `if`-`then`-`else` *can* be written: + - On a single line + - With the *body* after the `if`/`then`/`else` keyword + - With the *body* indented on the next line + - *Always* parenthesise the `else` part if it is a compound formula. +1. The `and` and `else` keywords *may* be placed on the same line as the closing parenthesis. +1. The `and` and `else` keywords *may* be "cuddled": `) else (` +1. *Always* qualify *calls* to predicates of the same class with `this`. +2. *Prefer* postfix casts `a.(Expr)` to prefix casts `(Expr)a`. + +### Examples + +```ql + argumentType.isImplicitlyConvertibleTo(parameterType) + or + argumentType instanceof NullType and + result.getParameter(i).isOut() and + parameterType instanceof SimpleType + or + reflectionOrDynamicArg(argumentType, parameterType) +``` + +```ql + this.getName() = "Finalize" and not exists(this.getAParameter()) +``` + +```ql + e1.getType() instanceof BoolType and ( + b1 = true + or + b1 = false + ) and ( + b2 = true + or + b2 = false + ) +``` + +```ql + if e1 instanceof BitwiseOrExpr or e1 instanceof LogicalOrExpr then ( + impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and + b1 = false + ) else ( + e1.getType() instanceof BoolType and + e1 = e2 and + b1 = b2 and + (b1 = true or b1 = false) + ) +``` + +```ql + (x instanceof Exception implies x.isPublic()) and y instanceof Exception +``` + +```ql + x instanceof Exception implies (x.isPublic() and y instanceof Exception) +``` + +```ql + exists(Type arg | arg = this.getAChild() | arg instanceof TypeParameter) +``` + +```ql + exists(Type qualifierType | + this.hasNonExactQualifierType(qualifierType) | + result = getANonExactQualifierSubType(qualifierType) + ) +``` + +```ql + methods = count(Method m | t = m.getDeclaringType() and not ilc(m)) +``` + +```ql + if n = 0 then result = 1 else result = n * f(n - 1) +``` + +```ql + if n = 0 + then result = 1 + else result = n * f(n - 1) +``` + +```ql + if + n = 0 + then + result = 1 + else + result = n * f(n - 1) +``` + +```ql + if exists(this.getContainingType()) then ( + result = "A nested class" and + parentName = this.getContainingType().getFullyQualifiedName() + ) else ( + result = parentName + "." + this.getName() and + parentName = this.getNamespace().getFullyQualifiedName() + ) +``` + +## Glossary + +| Phrase | Meaning | +|-------------|----------| +| *[annotation](https://help.semmle.com/QL/QLLanguageSpecification.html#annotations)* | An additional specifier used to modify a declaration, such as `private`, `override`, `deprecated`, `pragma`, `bindingset`, or `cached`. | +| *body* | The text inside `{ }`, `( )`, or each section of an `if`-`then`-`else` or `from`-`where`-`select`. | +| *binary operator* | An operator with two operands, such as comparison operators, `and`, `or`, `implies`, or arithmetic operators. | +| *call* | A *formula* that invokes a predicate, e.g. `this.isStatic()` or `calls(a,b)`. | +| *[conjunct](https://help.semmle.com/QL/QLLanguageSpecification.html#conjunctions)* | A formula that is an operand to an `and`. | +| *declaration* | A class, module, predicate, field or newtype. | +| *[disjunct](https://help.semmle.com/QL/QLLanguageSpecification.html#disjunctions)* | A formula that is an operand to an `or`. | +| *[formula](https://help.semmle.com/QL/QLLanguageSpecification.html#formulas)* | A logical expression, such as `A = B`, a *call*, a *quantifier*, `and`, `or`, `not`, `in` or `instanceof`. | +| *should/should not/avoid/prefer* | Adhere to this rule wherever possible, where it makes sense. | +| *may/can* | This is a reasonable alternative, to be used with discretion. | +| *must/always/do not* | Always adhere to this rule. | +| *[quantifier/aggregation](https://help.semmle.com/QL/QLLanguageSpecification.html#aggregations)* | `exists`, `count`, `strictcount`, `any`, `forall`, `forex` and so on. | +| *variable* | A parameter to a predicate, a field, a from variable, or a variable introduced by a *quantifier* or *aggregation*. | diff --git a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/MissingParentBean.xml b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/MissingParentBean.xml index 9e7554e52547..6ed09e2920ea 100644 --- a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/MissingParentBean.xml +++ b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/MissingParentBean.xml @@ -1,28 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/TooManyBeans.xml b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/TooManyBeans.xml index bd8b0ad7da1d..0361ff562c07 100644 --- a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/TooManyBeans.xml +++ b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/TooManyBeans.xml @@ -1,8 +1,8 @@ - - - - - - - - + + + + + + + + diff --git a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UnusedBean.xml b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UnusedBean.xml index e40477b83c80..39ef911a3e65 100644 --- a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UnusedBean.xml +++ b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UnusedBean.xml @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UselessPropertyOverride.xml b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UselessPropertyOverride.xml index 9337a0f045d2..9a8cceaf3dcd 100644 --- a/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UselessPropertyOverride.xml +++ b/java/ql/src/Frameworks/Spring/Architecture/Refactoring Opportunities/UselessPropertyOverride.xml @@ -1,23 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/AvoidAutowiring.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/AvoidAutowiring.xml index e7f707bdb650..2a10a0be651e 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/AvoidAutowiring.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/AvoidAutowiring.xml @@ -1,13 +1,13 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/DontUseConstructorArgIndex.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/DontUseConstructorArgIndex.xml index 3e14a36dbce2..92560855a7c3 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/DontUseConstructorArgIndex.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/DontUseConstructorArgIndex.xml @@ -1,13 +1,13 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/ImportsFirst.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/ImportsFirst.xml index 739e375f28c2..1199bb4de0eb 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/ImportsFirst.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/ImportsFirst.xml @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/NoBeanDescription.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/NoBeanDescription.xml index 438d11b4449c..7d6af942ca4b 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/NoBeanDescription.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/NoBeanDescription.xml @@ -1,27 +1,27 @@ - - - - This file configures the various service beans. - - - - - - This bean defines base properties common to the service beans - - ... - - - - ... - - - - ... - + + + + This file configures the various service beans. + + + + + + This bean defines base properties common to the service beans + + ... + + + + ... + + + + ... + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseIdInsteadOfName.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseIdInsteadOfName.xml index af75a2d37dd3..f61217937814 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseIdInsteadOfName.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseIdInsteadOfName.xml @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseLocalRef.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseLocalRef.xml index b2dcdea21194..5b6256ee52c5 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseLocalRef.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseLocalRef.xml @@ -1,17 +1,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseSetterInjection.java b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseSetterInjection.java index d5cf0de1ea6c..784b8ff92628 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseSetterInjection.java +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseSetterInjection.java @@ -1,22 +1,22 @@ -// Class for bean 'chart1' -public class WrongChartMaker { - private AxisRenderer axisRenderer = new DefaultAxisRenderer(); - private TrendRenderer trendRenderer = new DefaultTrendRenderer(); - - public WrongChartMaker() {} - - // Each combination of the optional parameters must be represented by a constructor. - public WrongChartMaker(AxisRenderer customAxisRenderer) { - this.axisRenderer = customAxisRenderer; - } - - public WrongChartMaker(TrendRenderer customTrendRenderer) { - this.trendRenderer = customTrendRenderer; - } - - public WrongChartMaker(AxisRenderer customAxisRenderer, - TrendRenderer customTrendRenderer) { - this.axisRenderer = customAxisRenderer; - this.trendRenderer = customTrendRenderer; - } +// Class for bean 'chart1' +public class WrongChartMaker { + private AxisRenderer axisRenderer = new DefaultAxisRenderer(); + private TrendRenderer trendRenderer = new DefaultTrendRenderer(); + + public WrongChartMaker() {} + + // Each combination of the optional parameters must be represented by a constructor. + public WrongChartMaker(AxisRenderer customAxisRenderer) { + this.axisRenderer = customAxisRenderer; + } + + public WrongChartMaker(TrendRenderer customTrendRenderer) { + this.trendRenderer = customTrendRenderer; + } + + public WrongChartMaker(AxisRenderer customAxisRenderer, + TrendRenderer customTrendRenderer) { + this.axisRenderer = customAxisRenderer; + this.trendRenderer = customTrendRenderer; + } } \ No newline at end of file diff --git a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseShortcutForms.xml b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseShortcutForms.xml index 703e67ffe935..0f6afa629f6c 100644 --- a/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseShortcutForms.xml +++ b/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseShortcutForms.xml @@ -1,38 +1,38 @@ - - - - main_service_registry - - - Top-level registry for services - - - - - - orderService - - com.foo.bar.OrderService - - - - billingService - - com.foo.bar.BillingService - - - - - - - - - - - - - - - - - + + + + main_service_registry + + + Top-level registry for services + + + + + + orderService + + com.foo.bar.OrderService + + + + billingService + + com.foo.bar.BillingService + + + + + + + + + + + + + + + + + diff --git a/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.java b/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.java index 6a310cfb758e..9541f489ea63 100644 --- a/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.java +++ b/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.java @@ -1,9 +1,9 @@ -// bean class -public class ContentService { - private TransactionHelper helper; - - // This method does not match the property in the bean file. - public void setHelper(TransactionHelper helper) { - this.helper = helper; - } -} +// bean class +public class ContentService { + private TransactionHelper helper; + + // This method does not match the property in the bean file. + public void setHelper(TransactionHelper helper) { + this.helper = helper; + } +} diff --git a/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.xml b/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.xml index 9a4ac59115c5..42a6b51ca029 100644 --- a/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.xml +++ b/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.xml @@ -1,7 +1,7 @@ - - - - - - + + + + + + diff --git a/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.java b/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.java index 96ab5134a369..ea5aa657ba01 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.java +++ b/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.java @@ -1,17 +1,17 @@ -public static void main(String args[]) { - Random r = new Random(); - - // BAD: 'mayBeNegativeInt' is negative if - // 'nextInt()' returns 'Integer.MIN_VALUE'. - int mayBeNegativeInt = Math.abs(r.nextInt()); - - // GOOD: 'nonNegativeInt' is always a value between 0 (inclusive) - // and Integer.MAX_VALUE (exclusive). - int nonNegativeInt = r.nextInt(Integer.MAX_VALUE); - - // GOOD: When 'nextInt' returns a negative number increment the returned value. - int nextInt = r.nextInt(); - if(nextInt < 0) - nextInt++; - int nonNegativeInt = Math.abs(nextInt); -} +public static void main(String args[]) { + Random r = new Random(); + + // BAD: 'mayBeNegativeInt' is negative if + // 'nextInt()' returns 'Integer.MIN_VALUE'. + int mayBeNegativeInt = Math.abs(r.nextInt()); + + // GOOD: 'nonNegativeInt' is always a value between 0 (inclusive) + // and Integer.MAX_VALUE (exclusive). + int nonNegativeInt = r.nextInt(Integer.MAX_VALUE); + + // GOOD: When 'nextInt' returns a negative number increment the returned value. + int nextInt = r.nextInt(); + if(nextInt < 0) + nextInt++; + int nonNegativeInt = Math.abs(nextInt); +} diff --git a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.java b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.java index 63eaedb77177..bb1886a80fe5 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.java +++ b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.java @@ -1,12 +1,12 @@ -public static void main(String args[]) { - // BAD: A new 'Random' object is created every time - // a pseudo-random integer is required. - int notReallyRandom = new Random().nextInt(); - int notReallyRandom2 = new Random().nextInt(); - - // GOOD: The same 'Random' object is used to generate - // two pseudo-random integers. - Random r = new Random(); - int random1 = r.nextInt(); - int random2 = r.nextInt(); +public static void main(String args[]) { + // BAD: A new 'Random' object is created every time + // a pseudo-random integer is required. + int notReallyRandom = new Random().nextInt(); + int notReallyRandom2 = new Random().nextInt(); + + // GOOD: The same 'Random' object is used to generate + // two pseudo-random integers. + Random r = new Random(); + int random1 = r.nextInt(); + int random2 = r.nextInt(); } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Finalization/NullifiedSuperFinalize.java b/java/ql/src/Likely Bugs/Finalization/NullifiedSuperFinalize.java index ddd6d5c7f4f1..811e3cdcac66 100644 --- a/java/ql/src/Likely Bugs/Finalization/NullifiedSuperFinalize.java +++ b/java/ql/src/Likely Bugs/Finalization/NullifiedSuperFinalize.java @@ -1,30 +1,30 @@ -class LocalCache { - private Collection localResources; - - //... - - protected void finalize() throws Throwable { - for (NativeResource r : localResources) { - r.dispose(); - } - }; -} - -class WrongCache extends LocalCache { - //... - @Override - protected void finalize() throws Throwable { - // BAD: Empty 'finalize', which does not call 'super.finalize'. - // Native resources in LocalCache are not disposed of. - } -} - -class RightCache extends LocalCache { - //... - @Override - protected void finalize() throws Throwable { - // GOOD: 'finalize' calls 'super.finalize'. - // Native resources in LocalCache are disposed of. - super.finalize(); - } +class LocalCache { + private Collection localResources; + + //... + + protected void finalize() throws Throwable { + for (NativeResource r : localResources) { + r.dispose(); + } + }; +} + +class WrongCache extends LocalCache { + //... + @Override + protected void finalize() throws Throwable { + // BAD: Empty 'finalize', which does not call 'super.finalize'. + // Native resources in LocalCache are not disposed of. + } +} + +class RightCache extends LocalCache { + //... + @Override + protected void finalize() throws Throwable { + // GOOD: 'finalize' calls 'super.finalize'. + // Native resources in LocalCache are disposed of. + super.finalize(); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Frameworks/JUnit/BadSuiteMethod.java b/java/ql/src/Likely Bugs/Frameworks/JUnit/BadSuiteMethod.java index 2cc578bfff78..9b17c5faf0c2 100644 --- a/java/ql/src/Likely Bugs/Frameworks/JUnit/BadSuiteMethod.java +++ b/java/ql/src/Likely Bugs/Frameworks/JUnit/BadSuiteMethod.java @@ -1,21 +1,21 @@ -public class BadSuiteMethod extends TestCase { - // BAD: JUnit 3.8 does not detect the following method as a 'suite' method. - // The method should be public, static, and return 'junit.framework.Test' - // or one of its subtypes. - static Test suite() { - TestSuite suite = new TestSuite(); - suite.addTest(new MyTests("testEquals")); - suite.addTest(new MyTests("testNotEquals")); - return suite; - } -} - -public class CorrectSuiteMethod extends TestCase { - // GOOD: JUnit 3.8 correctly detects the following method as a 'suite' method. - public static Test suite() { - TestSuite suite = new TestSuite(); - suite.addTest(new MyTests("testEquals")); - suite.addTest(new MyTests("testNotEquals")); - return suite; - } +public class BadSuiteMethod extends TestCase { + // BAD: JUnit 3.8 does not detect the following method as a 'suite' method. + // The method should be public, static, and return 'junit.framework.Test' + // or one of its subtypes. + static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTest(new MyTests("testEquals")); + suite.addTest(new MyTests("testNotEquals")); + return suite; + } +} + +public class CorrectSuiteMethod extends TestCase { + // GOOD: JUnit 3.8 correctly detects the following method as a 'suite' method. + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.addTest(new MyTests("testEquals")); + suite.addTest(new MyTests("testNotEquals")); + return suite; + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Frameworks/JUnit/TearDownNoSuper.java b/java/ql/src/Likely Bugs/Frameworks/JUnit/TearDownNoSuper.java index d05d4f163260..2a7ebea4d782 100644 --- a/java/ql/src/Likely Bugs/Frameworks/JUnit/TearDownNoSuper.java +++ b/java/ql/src/Likely Bugs/Frameworks/JUnit/TearDownNoSuper.java @@ -1,65 +1,65 @@ -// Abstract class that initializes then shuts down the -// framework after each set of tests -abstract class FrameworkTestCase extends TestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - Framework.init(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - Framework.shutdown(); - } -} - -// The following classes extend 'FrameworkTestCase' to reuse the -// 'setUp' and 'tearDown' methods of the framework. - -public class TearDownNoSuper extends FrameworkTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - public void testFramework() { - //... - } - - public void testFramework2() { - //... - } - - @Override - protected void tearDown() throws Exception { - // BAD: Does not call 'super.tearDown'. May cause later tests to fail - // when they try to re-initialize an already initialized framework. - // Even if the framework allows re-initialization, it may maintain the - // internal state, which could affect the results of succeeding tests. - System.out.println("Tests complete"); - } -} - -public class TearDownSuper extends FrameworkTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - public void testFramework() { - //... - } - - public void testFramework2() { - //... - } - - @Override - protected void tearDown() throws Exception { - // GOOD: Correctly calls 'super.tearDown' to shut down the - // framework. - System.out.println("Tests complete"); - super.tearDown(); - } +// Abstract class that initializes then shuts down the +// framework after each set of tests +abstract class FrameworkTestCase extends TestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + Framework.init(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + Framework.shutdown(); + } +} + +// The following classes extend 'FrameworkTestCase' to reuse the +// 'setUp' and 'tearDown' methods of the framework. + +public class TearDownNoSuper extends FrameworkTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testFramework() { + //... + } + + public void testFramework2() { + //... + } + + @Override + protected void tearDown() throws Exception { + // BAD: Does not call 'super.tearDown'. May cause later tests to fail + // when they try to re-initialize an already initialized framework. + // Even if the framework allows re-initialization, it may maintain the + // internal state, which could affect the results of succeeding tests. + System.out.println("Tests complete"); + } +} + +public class TearDownSuper extends FrameworkTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testFramework() { + //... + } + + public void testFramework2() { + //... + } + + @Override + protected void tearDown() throws Exception { + // GOOD: Correctly calls 'super.tearDown' to shut down the + // framework. + System.out.println("Tests complete"); + super.tearDown(); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Frameworks/JUnit/TestCaseNoTests.java b/java/ql/src/Likely Bugs/Frameworks/JUnit/TestCaseNoTests.java index d9ea2cb651da..9c6b6f403de7 100644 --- a/java/ql/src/Likely Bugs/Frameworks/JUnit/TestCaseNoTests.java +++ b/java/ql/src/Likely Bugs/Frameworks/JUnit/TestCaseNoTests.java @@ -1,28 +1,28 @@ -// BAD: This test case class does not have any valid JUnit 3.8 test methods. -public class TestCaseNoTests38 extends TestCase { - // This is not a test case because it does not start with 'test'. - public void simpleTest() { - //... - } - - // This is not a test case because it takes two parameters. - public void testNotEquals(int i, int j) { - assertEquals(i != j, true); - } - - // This is recognized as a test, but causes JUnit to fail - // when run because it is not public. - void testEquals() { - //... - } -} - -// GOOD: This test case class correctly declares test methods. -public class MyTests extends TestCase { - public void testEquals() { - assertEquals(1, 1); - } - public void testNotEquals() { - assertFalse(1 == 2); - } +// BAD: This test case class does not have any valid JUnit 3.8 test methods. +public class TestCaseNoTests38 extends TestCase { + // This is not a test case because it does not start with 'test'. + public void simpleTest() { + //... + } + + // This is not a test case because it takes two parameters. + public void testNotEquals(int i, int j) { + assertEquals(i != j, true); + } + + // This is recognized as a test, but causes JUnit to fail + // when run because it is not public. + void testEquals() { + //... + } +} + +// GOOD: This test case class correctly declares test methods. +public class MyTests extends TestCase { + public void testEquals() { + assertEquals(1, 1); + } + public void testNotEquals() { + assertFalse(1 == 2); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/I18N/MissingLocaleArgument.java b/java/ql/src/Likely Bugs/I18N/MissingLocaleArgument.java index 741bc708905f..382c80c209a4 100644 --- a/java/ql/src/Likely Bugs/I18N/MissingLocaleArgument.java +++ b/java/ql/src/Likely Bugs/I18N/MissingLocaleArgument.java @@ -1,14 +1,14 @@ -public static void main(String args[]) { - String phrase = "I miss my home in Mississippi."; - - // AVOID: Calling 'toLowerCase()' or 'toUpperCase()' - // produces different results depending on what the default locale is. - System.out.println(phrase.toUpperCase()); - System.out.println(phrase.toLowerCase()); - - // GOOD: Explicitly setting the locale when calling 'toLowerCase()' or - // 'toUpperCase()' ensures that the resulting string is - // English, regardless of the default locale. - System.out.println(phrase.toLowerCase(Locale.ENGLISH)); - System.out.println(phrase.toUpperCase(Locale.ENGLISH)); +public static void main(String args[]) { + String phrase = "I miss my home in Mississippi."; + + // AVOID: Calling 'toLowerCase()' or 'toUpperCase()' + // produces different results depending on what the default locale is. + System.out.println(phrase.toUpperCase()); + System.out.println(phrase.toLowerCase()); + + // GOOD: Explicitly setting the locale when calling 'toLowerCase()' or + // 'toUpperCase()' ensures that the resulting string is + // English, regardless of the default locale. + System.out.println(phrase.toLowerCase(Locale.ENGLISH)); + System.out.println(phrase.toUpperCase(Locale.ENGLISH)); } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerialVersionUID.java b/java/ql/src/Likely Bugs/Serialization/IncorrectSerialVersionUID.java index 91ee7d116cd3..eaf9e459780e 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerialVersionUID.java +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerialVersionUID.java @@ -1,11 +1,11 @@ -class WrongNote implements Serializable { - // BAD: serialVersionUID must be static, final, and 'long' - private static final int serialVersionUID = 1; - - //... -} - -class Note implements Serializable { - // GOOD: serialVersionUID is of the correct type - private static final long serialVersionUID = 1L; +class WrongNote implements Serializable { + // BAD: serialVersionUID must be static, final, and 'long' + private static final int serialVersionUID = 1; + + //... +} + +class Note implements Serializable { + // GOOD: serialVersionUID is of the correct type + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java index 33599f15e257..57fd8a64cc2f 100644 --- a/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java +++ b/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.java @@ -1,25 +1,25 @@ -class WrongNetRequest implements Serializable { - // BAD: Does not match the exact signature required for a custom - // deserialization protocol. Will not be called during deserialization. - void readObject(ObjectInputStream in) { - //... - } - - // BAD: Does not match the exact signature required for a custom - // serialization protocol. Will not be called during serialization. - protected void writeObject(ObjectOutputStream out) { - //... - } -} - -class NetRequest implements Serializable { - // GOOD: Signature for a custom deserialization implementation. - private void readObject(ObjectInputStream in) { - //... - } - - // GOOD: Signature for a custom serialization implementation. - private void writeObject(ObjectOutputStream out) { - //... - } +class WrongNetRequest implements Serializable { + // BAD: Does not match the exact signature required for a custom + // deserialization protocol. Will not be called during deserialization. + void readObject(ObjectInputStream in) { + //... + } + + // BAD: Does not match the exact signature required for a custom + // serialization protocol. Will not be called during serialization. + protected void writeObject(ObjectOutputStream out) { + //... + } +} + +class NetRequest implements Serializable { + // GOOD: Signature for a custom deserialization implementation. + private void readObject(ObjectInputStream in) { + //... + } + + // GOOD: Signature for a custom serialization implementation. + private void writeObject(ObjectOutputStream out) { + //... + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorOnExternalizable.java b/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorOnExternalizable.java index fc297ce17881..24c8d634d212 100644 --- a/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorOnExternalizable.java +++ b/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorOnExternalizable.java @@ -1,37 +1,37 @@ -class WrongMemo implements Externalizable { - private String memo; - - // BAD: No public no-argument constructor is defined. Deserializing this object - // causes an 'InvalidClassException'. - - public WrongMemo(String memo) { - this.memo = memo; - } - - public void writeExternal(ObjectOutput arg0) throws IOException { - //... - } - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - //... - } -} - -class Memo implements Externalizable { - private String memo; - - // GOOD: Declare a public no-argument constructor, which is used by the - // serialization framework when the object is deserialized. - public Memo() { - } - - public Memo(String memo) { - this.memo = memo; - } - - public void writeExternal(ObjectOutput out) throws IOException { - //... - } - public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { - //... - } +class WrongMemo implements Externalizable { + private String memo; + + // BAD: No public no-argument constructor is defined. Deserializing this object + // causes an 'InvalidClassException'. + + public WrongMemo(String memo) { + this.memo = memo; + } + + public void writeExternal(ObjectOutput arg0) throws IOException { + //... + } + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + //... + } +} + +class Memo implements Externalizable { + private String memo; + + // GOOD: Declare a public no-argument constructor, which is used by the + // serialization framework when the object is deserialized. + public Memo() { + } + + public Memo(String memo) { + this.memo = memo; + } + + public void writeExternal(ObjectOutput out) throws IOException { + //... + } + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + //... + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorsOnSerializable.java b/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorsOnSerializable.java index d9161f38710a..fea0087ba4fe 100644 --- a/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorsOnSerializable.java +++ b/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorsOnSerializable.java @@ -1,42 +1,42 @@ -class WrongItem { - private String name; - - // BAD: This class does not have a no-argument constructor, and throws an - // 'InvalidClassException' at runtime. - - public WrongItem(String name) { - this.name = name; - } -} - -class WrongSubItem extends WrongItem implements Serializable { - public WrongSubItem() { - super(null); - } - - public WrongSubItem(String name) { - super(name); - } -} - -class Item { - private String name; - - // GOOD: This class declares a no-argument constructor, which allows serializable - // subclasses to be deserialized without error. - public Item() {} - - public Item(String name) { - this.name = name; - } -} - -class SubItem extends Item implements Serializable { - public SubItem() { - super(null); - } - - public SubItem(String name) { - super(name); - } +class WrongItem { + private String name; + + // BAD: This class does not have a no-argument constructor, and throws an + // 'InvalidClassException' at runtime. + + public WrongItem(String name) { + this.name = name; + } +} + +class WrongSubItem extends WrongItem implements Serializable { + public WrongSubItem() { + super(null); + } + + public WrongSubItem(String name) { + super(name); + } +} + +class Item { + private String name; + + // GOOD: This class declares a no-argument constructor, which allows serializable + // subclasses to be deserialized without error. + public Item() {} + + public Item(String name) { + this.name = name; + } +} + +class SubItem extends Item implements Serializable { + public SubItem() { + super(null); + } + + public SubItem(String name) { + super(name); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/NonSerializableComparator.java b/java/ql/src/Likely Bugs/Serialization/NonSerializableComparator.java index 1617440e762d..d2d758bcebc1 100644 --- a/java/ql/src/Likely Bugs/Serialization/NonSerializableComparator.java +++ b/java/ql/src/Likely Bugs/Serialization/NonSerializableComparator.java @@ -1,16 +1,16 @@ -// BAD: This is not serializable, and throws a 'java.io.NotSerializableException' -// when used in a serializable sorted collection. -class WrongComparator implements Comparator { - public int compare(String o1, String o2) { - return o1.compareTo(o2); - } -} - -// GOOD: This is serializable, and can be used in collections that are meant to be serialized. -class StringComparator implements Comparator, Serializable { - private static final long serialVersionUID = -5972458403679726498L; - - public int compare(String arg0, String arg1) { - return arg0.compareTo(arg1); - } +// BAD: This is not serializable, and throws a 'java.io.NotSerializableException' +// when used in a serializable sorted collection. +class WrongComparator implements Comparator { + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } +} + +// GOOD: This is serializable, and can be used in collections that are meant to be serialized. +class StringComparator implements Comparator, Serializable { + private static final long serialVersionUID = -5972458403679726498L; + + public int compare(String arg0, String arg1) { + return arg0.compareTo(arg1); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/NonSerializableField.java b/java/ql/src/Likely Bugs/Serialization/NonSerializableField.java index 999e0dc207a0..10821b8d7851 100644 --- a/java/ql/src/Likely Bugs/Serialization/NonSerializableField.java +++ b/java/ql/src/Likely Bugs/Serialization/NonSerializableField.java @@ -1,27 +1,27 @@ -class DerivedFactors { // Class that contains derived values computed from entries in a - private Number efficiency; // performance record - private Number costPerItem; - private Number profitPerItem; - ... -} - -class WrongPerformanceRecord implements Serializable { - private String unitId; - private Number dailyThroughput; - private Number dailyCost; - private DerivedFactors factors; // BAD: 'DerivedFactors' is not serializable - // but is in a serializable class. This - // causes a 'java.io.NotSerializableException' - // when 'WrongPerformanceRecord' is serialized. - ... -} - -class PerformanceRecord implements Serializable { - private String unitId; - private Number dailyThroughput; - private Number dailyCost; - transient private DerivedFactors factors; // GOOD: 'DerivedFactors' is declared - // 'transient' so it does not contribute to the - // serializable state of 'PerformanceRecord'. - ... -} +class DerivedFactors { // Class that contains derived values computed from entries in a + private Number efficiency; // performance record + private Number costPerItem; + private Number profitPerItem; + ... +} + +class WrongPerformanceRecord implements Serializable { + private String unitId; + private Number dailyThroughput; + private Number dailyCost; + private DerivedFactors factors; // BAD: 'DerivedFactors' is not serializable + // but is in a serializable class. This + // causes a 'java.io.NotSerializableException' + // when 'WrongPerformanceRecord' is serialized. + ... +} + +class PerformanceRecord implements Serializable { + private String unitId; + private Number dailyThroughput; + private Number dailyCost; + transient private DerivedFactors factors; // GOOD: 'DerivedFactors' is declared + // 'transient' so it does not contribute to the + // serializable state of 'PerformanceRecord'. + ... +} diff --git a/java/ql/src/Likely Bugs/Serialization/NonSerializableFieldTooGeneral.java b/java/ql/src/Likely Bugs/Serialization/NonSerializableFieldTooGeneral.java index b2d51cf73e86..1a4a5cc04ca3 100644 --- a/java/ql/src/Likely Bugs/Serialization/NonSerializableFieldTooGeneral.java +++ b/java/ql/src/Likely Bugs/Serialization/NonSerializableFieldTooGeneral.java @@ -1,29 +1,29 @@ -class WrongPair implements Serializable{ - private final L left; // BAD - private final R right; // BAD: L and R are not guaranteed to be serializable - - public WrongPair(L left, R right){ ... } - - ... -} - -class Pair implements Serializable{ - private final L left; // GOOD: L and R must implement Serializable - private final R right; - - public Pair(L left, R right){ ... } - - ... -} - -class WrongEvent implements Serializable{ - private Object eventData; // BAD: Type is too general. - - public WrongEvent(Object eventData){ ... } -} - -class Event implements Serializable{ - private Serializable eventData; // GOOD: Force the user to supply only serializable data - - public Event(Serializable eventData){ ... } -} +class WrongPair implements Serializable{ + private final L left; // BAD + private final R right; // BAD: L and R are not guaranteed to be serializable + + public WrongPair(L left, R right){ ... } + + ... +} + +class Pair implements Serializable{ + private final L left; // GOOD: L and R must implement Serializable + private final R right; + + public Pair(L left, R right){ ... } + + ... +} + +class WrongEvent implements Serializable{ + private Object eventData; // BAD: Type is too general. + + public WrongEvent(Object eventData){ ... } +} + +class Event implements Serializable{ + private Serializable eventData; // GOOD: Force the user to supply only serializable data + + public Event(Serializable eventData){ ... } +} diff --git a/java/ql/src/Likely Bugs/Serialization/NonSerializableInnerClass.java b/java/ql/src/Likely Bugs/Serialization/NonSerializableInnerClass.java index d2d441108128..e9a66f56a4a3 100644 --- a/java/ql/src/Likely Bugs/Serialization/NonSerializableInnerClass.java +++ b/java/ql/src/Likely Bugs/Serialization/NonSerializableInnerClass.java @@ -1,33 +1,33 @@ -class NonSerializableServer { - - // BAD: The following class is serializable, but the enclosing class - // 'NonSerializableServer' is not. Serializing an instance of 'WrongSession' - // causes a 'java.io.NotSerializableException'. - class WrongSession implements Serializable { - private static final long serialVersionUID = 8970783971992397218L; - private int id; - private String user; - - WrongSession(int id, String user) { /*...*/ } - } - - public WrongSession getNewSession(String user) { - return new WrongSession(newId(), user); - } -} - -class Server { - - // GOOD: The following class can be correctly serialized because it is static. - static class Session implements Serializable { - private static final long serialVersionUID = 1065454318648105638L; - private int id; - private String user; - - Session(int id, String user) { /*...*/ } - } - - public Session getNewSession(String user) { - return new Session(newId(), user); - } +class NonSerializableServer { + + // BAD: The following class is serializable, but the enclosing class + // 'NonSerializableServer' is not. Serializing an instance of 'WrongSession' + // causes a 'java.io.NotSerializableException'. + class WrongSession implements Serializable { + private static final long serialVersionUID = 8970783971992397218L; + private int id; + private String user; + + WrongSession(int id, String user) { /*...*/ } + } + + public WrongSession getNewSession(String user) { + return new WrongSession(newId(), user); + } +} + +class Server { + + // GOOD: The following class can be correctly serialized because it is static. + static class Session implements Serializable { + private static final long serialVersionUID = 1065454318648105638L; + private int id; + private String user; + + Session(int id, String user) { /*...*/ } + } + + public Session getNewSession(String user) { + return new Session(newId(), user); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/ReadResolveObject.java b/java/ql/src/Likely Bugs/Serialization/ReadResolveObject.java index cba64daf49c1..25057e1ecf28 100644 --- a/java/ql/src/Likely Bugs/Serialization/ReadResolveObject.java +++ b/java/ql/src/Likely Bugs/Serialization/ReadResolveObject.java @@ -1,40 +1,40 @@ -class FalseSingleton implements Serializable { - private static final long serialVersionUID = -7480651116825504381L; - private static FalseSingleton instance; - - private FalseSingleton() {} - - public static FalseSingleton getInstance() { - if (instance == null) { - instance = new FalseSingleton(); - } - return instance; - } - - // BAD: Signature of 'readResolve' does not match the exact signature that is expected - // (that is, it does not return 'java.lang.Object'). - public FalseSingleton readResolve() throws ObjectStreamException { - return FalseSingleton.getInstance(); - } -} - -class Singleton implements Serializable { - private static final long serialVersionUID = -7480651116825504381L; - private static Singleton instance; - - private Singleton() {} - - public static Singleton getInstance() { - if (instance == null) { - instance = new Singleton(); - } - return instance; - } - - // GOOD: Signature of 'readResolve' matches the exact signature that is expected. - // It replaces the singleton that is read from a stream with an instance of 'Singleton', - // instead of creating a new singleton. - private Object readResolve() throws ObjectStreamException { - return Singleton.getInstance(); - } +class FalseSingleton implements Serializable { + private static final long serialVersionUID = -7480651116825504381L; + private static FalseSingleton instance; + + private FalseSingleton() {} + + public static FalseSingleton getInstance() { + if (instance == null) { + instance = new FalseSingleton(); + } + return instance; + } + + // BAD: Signature of 'readResolve' does not match the exact signature that is expected + // (that is, it does not return 'java.lang.Object'). + public FalseSingleton readResolve() throws ObjectStreamException { + return FalseSingleton.getInstance(); + } +} + +class Singleton implements Serializable { + private static final long serialVersionUID = -7480651116825504381L; + private static Singleton instance; + + private Singleton() {} + + public static Singleton getInstance() { + if (instance == null) { + instance = new Singleton(); + } + return instance; + } + + // GOOD: Signature of 'readResolve' matches the exact signature that is expected. + // It replaces the singleton that is read from a stream with an instance of 'Singleton', + // instead of creating a new singleton. + private Object readResolve() throws ObjectStreamException { + return Singleton.getInstance(); + } } \ No newline at end of file diff --git a/java/ql/src/Likely Bugs/Serialization/TransientNotSerializable.java b/java/ql/src/Likely Bugs/Serialization/TransientNotSerializable.java index 783ed4f41814..3025f29bd5e6 100644 --- a/java/ql/src/Likely Bugs/Serialization/TransientNotSerializable.java +++ b/java/ql/src/Likely Bugs/Serialization/TransientNotSerializable.java @@ -1,12 +1,12 @@ -class State { - // The 'transient' modifier has no effect here because - // the 'State' class does not implement 'Serializable'. - private transient int[] stateData; -} - -class PersistentState implements Serializable { - private int[] stateData; - // The 'transient' modifier indicates that this field is not part of - // the persistent state and should therefore not be serialized. - private transient int[] cachedComputedData; +class State { + // The 'transient' modifier has no effect here because + // the 'State' class does not implement 'Serializable'. + private transient int[] stateData; +} + +class PersistentState implements Serializable { + private int[] stateData; + // The 'transient' modifier indicates that this field is not part of + // the persistent state and should therefore not be serialized. + private transient int[] cachedComputedData; } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.java b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.java index d55d76f835f8..7213d78104b2 100644 --- a/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.java +++ b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.java @@ -1,14 +1,14 @@ -class FinalizedClass { - Object o = new Object(); - String s = "abcdefg"; - Integer i = Integer.valueOf(2); - - @Override - protected void finalize() throws Throwable { - super.finalize(); - //No need to nullify fields - this.o = null; - this.s = null; - this.i = null; - } +class FinalizedClass { + Object o = new Object(); + String s = "abcdefg"; + Integer i = Integer.valueOf(2); + + @Override + protected void finalize() throws Throwable { + super.finalize(); + //No need to nullify fields + this.o = null; + this.s = null; + this.i = null; + } } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-comment.java b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-comment.java index a71288df47d1..75f45e61ffcb 100644 --- a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-comment.java +++ b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-comment.java @@ -1,9 +1,9 @@ -synchronized void waitIfAutoSyncScheduled() { - try { - while (isAutoSyncScheduled) { - this.wait(1000); - } - } catch (InterruptedException e) { - // Expected exception. The file cannot be synchronized yet. - } +synchronized void waitIfAutoSyncScheduled() { + try { + while (isAutoSyncScheduled) { + this.wait(1000); + } + } catch (InterruptedException e) { + // Expected exception. The file cannot be synchronized yet. + } } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-good.java b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-good.java index 0085f15c2dba..4aeb0f464c93 100644 --- a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-good.java +++ b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-good.java @@ -1,10 +1,10 @@ -// Exception is passed to 'ignore' method with a comment -synchronized void waitIfAutoSyncScheduled() { - try { - while (isAutoSyncScheduled) { - this.wait(1000); - } - } catch (InterruptedException e) { - Exceptions.ignore(e, "Expected exception. The file cannot be synchronized yet."); - } +// Exception is passed to 'ignore' method with a comment +synchronized void waitIfAutoSyncScheduled() { + try { + while (isAutoSyncScheduled) { + this.wait(1000); + } + } catch (InterruptedException e) { + Exceptions.ignore(e, "Expected exception. The file cannot be synchronized yet."); + } } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-ignore.java b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-ignore.java index 6985620e530e..266105c45c23 100644 --- a/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-ignore.java +++ b/java/ql/src/Violations of Best Practice/Exception Handling/DroppedExceptions-ignore.java @@ -1,5 +1,5 @@ -// 'ignore' method. This method does nothing, but can be called -// to document the reason why the exception can be ignored. -public static void ignore(Throwable e, String message) { - +// 'ignore' method. This method does nothing, but can be called +// to document the reason why the exception can be ignored. +public static void ignore(Throwable e, String message) { + } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.java b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.java index 06655aa8c1f9..0a7ec51c9201 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.java +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.java @@ -1,8 +1,8 @@ -void main() { - // ... - // BAD: Call to 'runFinalizersOnExit' forces execution of all finalizers on termination of - // the runtime, which can cause live objects to transition to an invalid state. - // Avoid using this method (and finalizers in general). - System.runFinalizersOnExit(true); - // ... +void main() { + // ... + // BAD: Call to 'runFinalizersOnExit' forces execution of all finalizers on termination of + // the runtime, which can cause live objects to transition to an invalid state. + // Avoid using this method (and finalizers in general). + System.runFinalizersOnExit(true); + // ... } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.java b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.java index 2fb4df112831..476ac9d49f56 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.java +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.java @@ -1,9 +1,9 @@ -public static void main(String args[]) { - String name = "John Doe"; - - // BAD: Unnecessary call to 'toString' on 'name' - System.out.println("Hi, my name is " + name.toString()); - - // GOOD: No call to 'toString' on 'name' - System.out.println("Hi, my name is " + name); +public static void main(String args[]) { + String name = "John Doe"; + + // BAD: Unnecessary call to 'toString' on 'name' + System.out.println("Hi, my name is " + name.toString()); + + // GOOD: No call to 'toString' on 'name' + System.out.println("Hi, my name is " + name); } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.java b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.java index 2cff927b1579..7363006363c1 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.java +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.java @@ -1,21 +1,21 @@ -// This class does not have a 'toString' method, so 'java.lang.Object.toString' -// is used when the class is converted to a string. -class WrongPerson { - private String name; - private Date birthDate; - - public WrongPerson(String name, Date birthDate) { - this.name =name; - this.birthDate = birthDate; - } -} - -public static void main(String args[]) throws Exception { - DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); - WrongPerson wp = new WrongPerson("Robert Van Winkle", dateFormatter.parse("1967-10-31")); - - // BAD: The following statement implicitly calls 'Object.toString', - // which returns something similar to: - // WrongPerson@4383f74d - System.out.println(wp); +// This class does not have a 'toString' method, so 'java.lang.Object.toString' +// is used when the class is converted to a string. +class WrongPerson { + private String name; + private Date birthDate; + + public WrongPerson(String name, Date birthDate) { + this.name =name; + this.birthDate = birthDate; + } +} + +public static void main(String args[]) throws Exception { + DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); + WrongPerson wp = new WrongPerson("Robert Van Winkle", dateFormatter.parse("1967-10-31")); + + // BAD: The following statement implicitly calls 'Object.toString', + // which returns something similar to: + // WrongPerson@4383f74d + System.out.println(wp); } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.java b/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.java index 47c881dbf204..16d0f46fe099 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.java +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.java @@ -1,15 +1,15 @@ -class RequestHandler extends Thread { - private boolean isRunning; - private Connection conn = new Connection(); - - public void run() { - while (isRunning) { - Request req = conn.getRequest(); - // Process the request ... - - System.gc(); // This call may cause a garbage collection after each request. - // This will likely reduce the throughput of the RequestHandler - // because the JVM spends time on unnecessary garbage collection passes. - } - } +class RequestHandler extends Thread { + private boolean isRunning; + private Connection conn = new Connection(); + + public void run() { + while (isRunning) { + Request req = conn.getRequest(); + // Process the request ... + + System.gc(); // This call may cause a garbage collection after each request. + // This will likely reduce the throughput of the RequestHandler + // because the JVM spends time on unnecessary garbage collection passes. + } + } } \ No newline at end of file diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.java b/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.java index 78f1221829a6..5ef908a23e8b 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.java +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.java @@ -1,24 +1,24 @@ -public static void main(String args[]) { - String[] words = {"Who", "is", "John", "Galt"}; - String[][] wordMatrix = {{"There", "is"}, {"no", "spoon"}}; - - // BAD: This implicitly uses 'Object.toString' to convert the contents - // of 'words[]', and prints out something similar to: - // [Ljava.lang.String;@459189e1 - System.out.println(words); - - // GOOD: 'Arrays.toString' calls 'toString' on - // each of the array's elements. The statement prints out: - // [Who, is, John, Galt] - System.out.println(Arrays.toString(words)); - - // ALMOST RIGHT: This calls 'toString' on each of the multi-dimensional - // array's elements. However, because the elements are arrays, the statement - // prints out something similar to: - // [[Ljava.lang.String;@55f33675, [Ljava.lang.String;@527c6768]] - System.out.println(Arrays.toString(wordMatrix)); - - // GOOD: This properly prints out the contents of the multi-dimensional array: - // [[There, is], [no, spoon]] - System.out.println(Arrays.deepToString(wordMatrix)); +public static void main(String args[]) { + String[] words = {"Who", "is", "John", "Galt"}; + String[][] wordMatrix = {{"There", "is"}, {"no", "spoon"}}; + + // BAD: This implicitly uses 'Object.toString' to convert the contents + // of 'words[]', and prints out something similar to: + // [Ljava.lang.String;@459189e1 + System.out.println(words); + + // GOOD: 'Arrays.toString' calls 'toString' on + // each of the array's elements. The statement prints out: + // [Who, is, John, Galt] + System.out.println(Arrays.toString(words)); + + // ALMOST RIGHT: This calls 'toString' on each of the multi-dimensional + // array's elements. However, because the elements are arrays, the statement + // prints out something similar to: + // [[Ljava.lang.String;@55f33675, [Ljava.lang.String;@527c6768]] + System.out.println(Arrays.toString(wordMatrix)); + + // GOOD: This properly prints out the contents of the multi-dimensional array: + // [[There, is], [no, spoon]] + System.out.println(Arrays.deepToString(wordMatrix)); } \ No newline at end of file diff --git a/java/ql/src/plugin.xml b/java/ql/src/plugin.xml index ff4510727795..e129a194b56f 100644 --- a/java/ql/src/plugin.xml +++ b/java/ql/src/plugin.xml @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/java/ql/test/library-tests/comments/.gitattributes b/java/ql/test/library-tests/comments/.gitattributes new file mode 100644 index 000000000000..1dd1b58fd4a3 --- /dev/null +++ b/java/ql/test/library-tests/comments/.gitattributes @@ -0,0 +1 @@ +TestWindows.java eol=crlf diff --git a/java/ql/test/library-tests/comments/TestWindows.java b/java/ql/test/library-tests/comments/TestWindows.java index cdefc327c5b7..2409adeed8f2 100644 --- a/java/ql/test/library-tests/comments/TestWindows.java +++ b/java/ql/test/library-tests/comments/TestWindows.java @@ -1,22 +1,22 @@ -/** - * A JavaDoc comment - * with multiple lines. - */ -class TestWindows { - /** A JavaDoc comment with a single line. */ - void m() { - // a single-line comment - // another single-line comment - } - - /* A block comment - * with multiple lines. - */ - - /* A block comment with a single line. */ - - // an end-of-line comment with a spurious trailing comment marker */ - // an end-of-line comment with trailing whitespace - //an end-of-line comment without a leading space - void test() {} // an end-of-line comment with preceding code -} +/** + * A JavaDoc comment + * with multiple lines. + */ +class TestWindows { + /** A JavaDoc comment with a single line. */ + void m() { + // a single-line comment + // another single-line comment + } + + /* A block comment + * with multiple lines. + */ + + /* A block comment with a single line. */ + + // an end-of-line comment with a spurious trailing comment marker */ + // an end-of-line comment with trailing whitespace + //an end-of-line comment without a leading space + void test() {} // an end-of-line comment with preceding code +} diff --git a/java/ql/test/library-tests/successors/CloseReaderTest/CloseReaderTest.java b/java/ql/test/library-tests/successors/CloseReaderTest/CloseReaderTest.java index f466bb265185..ab52205adf2f 100644 --- a/java/ql/test/library-tests/successors/CloseReaderTest/CloseReaderTest.java +++ b/java/ql/test/library-tests/successors/CloseReaderTest/CloseReaderTest.java @@ -1,25 +1,25 @@ -package successors; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; - -public class CloseReaderTest { - public static String readPassword(File keyFile) - { - // TODO: use Console.readPassword() when it's available. - System.out.print("Enter password for " + keyFile - + " (password will not be hidden): "); - System.out.flush(); - BufferedReader stdin = new BufferedReader(new InputStreamReader( - System.in)); - try - { - return stdin.readLine(); - } catch (IOException ex) - { - return null; - } - } -} +package successors; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +public class CloseReaderTest { + public static String readPassword(File keyFile) + { + // TODO: use Console.readPassword() when it's available. + System.out.print("Enter password for " + keyFile + + " (password will not be hidden): "); + System.out.flush(); + BufferedReader stdin = new BufferedReader(new InputStreamReader( + System.in)); + try + { + return stdin.readLine(); + } catch (IOException ex) + { + return null; + } + } +} diff --git a/java/ql/test/library-tests/successors/LoopVarReadTest/LoopVarReadTest.java b/java/ql/test/library-tests/successors/LoopVarReadTest/LoopVarReadTest.java index 6f8d45e8438a..c0a6d9bba4c6 100644 --- a/java/ql/test/library-tests/successors/LoopVarReadTest/LoopVarReadTest.java +++ b/java/ql/test/library-tests/successors/LoopVarReadTest/LoopVarReadTest.java @@ -1,16 +1,16 @@ -package successors; - -public class LoopVarReadTest { - public static void testLoop() - { - int x = 2; - for (int y = 0; y < 10; y += x) - { - System.out.println("Foo"); - } - - int q = 10; - - System.out.println("foo"); - } -} +package successors; + +public class LoopVarReadTest { + public static void testLoop() + { + int x = 2; + for (int y = 0; y < 10; y += x) + { + System.out.println("Foo"); + } + + int q = 10; + + System.out.println("foo"); + } +} diff --git a/java/ql/test/library-tests/successors/SaveFileTest/SaveFileTest.java b/java/ql/test/library-tests/successors/SaveFileTest/SaveFileTest.java index 02b4b46cdb2a..f07bf930f413 100644 --- a/java/ql/test/library-tests/successors/SaveFileTest/SaveFileTest.java +++ b/java/ql/test/library-tests/successors/SaveFileTest/SaveFileTest.java @@ -1,56 +1,56 @@ -package successors; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - - -public class SaveFileTest { - public void saveFile(String path, String contentType, - long size, InputStream is) throws FileNotFoundException, - IOException - { - - String savePath = path; - if (path.startsWith("/")) - { - savePath = path.substring(1); - } - - // make sure uploads area exists for this weblog - File dirPath = new File("foo"); - File saveFile = new File(dirPath.getAbsolutePath() + File.separator - + savePath); - - byte[] buffer = new byte[8192]; - int bytesRead = 0; - OutputStream bos = null; - try - { - bos = new FileOutputStream(saveFile); - while ((bytesRead = is.read(buffer, 0, 8192)) != -1) - { - bos.write(buffer, 0, bytesRead); - } - - System.out.println("The file has been written to [" - + saveFile.getAbsolutePath() + "]"); - } catch (Exception e) - { - throw new IOException("ERROR uploading file", e); - } finally - { - try - { - bos.flush(); - bos.close(); - } catch (Exception ignored) - { - } - } - - } -} +package successors; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +public class SaveFileTest { + public void saveFile(String path, String contentType, + long size, InputStream is) throws FileNotFoundException, + IOException + { + + String savePath = path; + if (path.startsWith("/")) + { + savePath = path.substring(1); + } + + // make sure uploads area exists for this weblog + File dirPath = new File("foo"); + File saveFile = new File(dirPath.getAbsolutePath() + File.separator + + savePath); + + byte[] buffer = new byte[8192]; + int bytesRead = 0; + OutputStream bos = null; + try + { + bos = new FileOutputStream(saveFile); + while ((bytesRead = is.read(buffer, 0, 8192)) != -1) + { + bos.write(buffer, 0, bytesRead); + } + + System.out.println("The file has been written to [" + + saveFile.getAbsolutePath() + "]"); + } catch (Exception e) + { + throw new IOException("ERROR uploading file", e); + } finally + { + try + { + bos.flush(); + bos.close(); + } catch (Exception ignored) + { + } + } + + } +} diff --git a/java/ql/test/library-tests/successors/TestBreak/TestBreak.java b/java/ql/test/library-tests/successors/TestBreak/TestBreak.java index e9bfc8aecf75..c4663bb21f33 100644 --- a/java/ql/test/library-tests/successors/TestBreak/TestBreak.java +++ b/java/ql/test/library-tests/successors/TestBreak/TestBreak.java @@ -1,86 +1,86 @@ -package successors; - -public class TestBreak { - public void f() - { - //loop breaks - a: - for (;;) - { - int x = 1; - x = x + 1; - if (x == 1) - { - break; - } else - { - for (int q : new int[20]) - { - if (q == 1) - { - break; - } else - { - break a; - } - } - } - } - int y = 12; - while (true) - { - if (y == 1) - { - break; - } else - { - do - { - if (y == 2) - { - break; - } - y = y + 2; - } while (y == 1); - y = 12; - } - } - y = 13; - - //switch breaks - int x =12; - switch (x) - { - case 1: - x = x + 1; - y = y + 1; - case 2: - x = x + 2; - y = y + 2; - break; - case 3: - case 4: - x = x + 3; - y = y + 4; - break; - case 5: - case 6: - x = x + 5; - y = y + 6; - default: - x = y; - y = x; - } - - //no default - switch(x) - { - case 1: - x = 1; - break; - case 2: - x = 2; - break; - } - } -} +package successors; + +public class TestBreak { + public void f() + { + //loop breaks + a: + for (;;) + { + int x = 1; + x = x + 1; + if (x == 1) + { + break; + } else + { + for (int q : new int[20]) + { + if (q == 1) + { + break; + } else + { + break a; + } + } + } + } + int y = 12; + while (true) + { + if (y == 1) + { + break; + } else + { + do + { + if (y == 2) + { + break; + } + y = y + 2; + } while (y == 1); + y = 12; + } + } + y = 13; + + //switch breaks + int x =12; + switch (x) + { + case 1: + x = x + 1; + y = y + 1; + case 2: + x = x + 2; + y = y + 2; + break; + case 3: + case 4: + x = x + 3; + y = y + 4; + break; + case 5: + case 6: + x = x + 5; + y = y + 6; + default: + x = y; + y = x; + } + + //no default + switch(x) + { + case 1: + x = 1; + break; + case 2: + x = 2; + break; + } + } +} diff --git a/java/ql/test/library-tests/successors/TestContinue/TestContinue.java b/java/ql/test/library-tests/successors/TestContinue/TestContinue.java index 5a0042b3540e..77e94e5c7ecf 100644 --- a/java/ql/test/library-tests/successors/TestContinue/TestContinue.java +++ b/java/ql/test/library-tests/successors/TestContinue/TestContinue.java @@ -1,59 +1,59 @@ -package successors; - -public class TestContinue { - public void f() - { - //loop breaks - a: - for (int p = 0; p < 10;) - { - int x = 1; - x = x + 1; - if (x == 1) - { - continue; - } else - { - for (int q : new int[20]) - { - if (q == 1) - { - continue; - } else if (q == 2) - { - continue a; - } - q = 12; - } - } - } - int y = 12; - while (y != 13) - { - if (y == 1) - { - continue; - } else - { - do - { - if (y == 2) - { - continue; - } - y = y + 2; - } while (y == 1); - y = 12; - } - y = 15; - } - y = 13; - while (y != 12) - { - if (y != 6) - continue; - else - break; - } - } -} +package successors; + +public class TestContinue { + public void f() + { + //loop breaks + a: + for (int p = 0; p < 10;) + { + int x = 1; + x = x + 1; + if (x == 1) + { + continue; + } else + { + for (int q : new int[20]) + { + if (q == 1) + { + continue; + } else if (q == 2) + { + continue a; + } + q = 12; + } + } + } + int y = 12; + while (y != 13) + { + if (y == 1) + { + continue; + } else + { + do + { + if (y == 2) + { + continue; + } + y = y + 2; + } while (y == 1); + y = 12; + } + y = 15; + } + y = 13; + while (y != 12) + { + if (y != 6) + continue; + else + break; + } + } +} diff --git a/java/ql/test/library-tests/successors/TestFinally/TestFinally.java b/java/ql/test/library-tests/successors/TestFinally/TestFinally.java index 19f67f64a9a9..51398bc7b8a0 100644 --- a/java/ql/test/library-tests/successors/TestFinally/TestFinally.java +++ b/java/ql/test/library-tests/successors/TestFinally/TestFinally.java @@ -1,150 +1,150 @@ -package successors; - -public class TestFinally { - public void f() - { - int z = 12; - try - { - try - { - System.out.println("Try1"); - if (z == 1) - { - return; - } - try - { - System.out.println("Try1"); - if (z == 1) - { - return; - } - System.out.println("Try2"); - } catch (Exception ex) - { - System.out.println("Exception"); - if (z == 1) - { - return; - } - } finally - { - System.out.println("Finally"); - if (z == 1) - { - return; - } - System.out.println("Finally2"); - } - System.out.println("Try2"); - } catch (Exception ex) - { - System.out.println("Exception"); - try - { - System.out.println("Try1"); - if (z == 1) - { - return; - } - System.out.println("Try2"); - } catch (Exception ex2) - { - System.out.println("Exception"); - if (z == 1) - { - return; - } - } finally - { - System.out.println("Finally"); - if (z == 1) - { - return; - } - System.out.println("Finally2"); - } - if (z == 1) - { - return; - } - } finally - { - System.out.println("Finally"); - if (z == 1) - { - return; - } - System.out.println("Finally2"); - } - System.out.println("Foo"); - int y = 12 + 3; - System.out.println("Bar"); - y = y + 1; - return; - } catch (Exception e) - { - try - { - System.out.println("Try1"); - if (z == 1) - { - return; - } - System.out.println("Try2"); - } catch (Exception ex) - { - System.out.println("Exception"); - if (z == 1) - { - return; - } - } finally - { - System.out.println("Finally"); - if (z == 1) - { - return; - } - System.out.println("Finally2"); - } - int x = 1; - System.out.println("Error: " + e); - x = x + 1; - } finally - { - int y = 12; - System.out.println("Finally"); - y = y + 1; - } - z = z + 1; - - try - { - System.out.println("Try1"); - if (z == 1) - { - return; - } - System.out.println("Try2"); - } catch (Exception ex) - { - System.out.println("Exception"); - if (z == 1) - { - return; - } - } finally - { - System.out.println("Finally"); - if (z == 1) - { - return; - } - System.out.println("Finally2"); - } - - z = z + 2; - } -} +package successors; + +public class TestFinally { + public void f() + { + int z = 12; + try + { + try + { + System.out.println("Try1"); + if (z == 1) + { + return; + } + try + { + System.out.println("Try1"); + if (z == 1) + { + return; + } + System.out.println("Try2"); + } catch (Exception ex) + { + System.out.println("Exception"); + if (z == 1) + { + return; + } + } finally + { + System.out.println("Finally"); + if (z == 1) + { + return; + } + System.out.println("Finally2"); + } + System.out.println("Try2"); + } catch (Exception ex) + { + System.out.println("Exception"); + try + { + System.out.println("Try1"); + if (z == 1) + { + return; + } + System.out.println("Try2"); + } catch (Exception ex2) + { + System.out.println("Exception"); + if (z == 1) + { + return; + } + } finally + { + System.out.println("Finally"); + if (z == 1) + { + return; + } + System.out.println("Finally2"); + } + if (z == 1) + { + return; + } + } finally + { + System.out.println("Finally"); + if (z == 1) + { + return; + } + System.out.println("Finally2"); + } + System.out.println("Foo"); + int y = 12 + 3; + System.out.println("Bar"); + y = y + 1; + return; + } catch (Exception e) + { + try + { + System.out.println("Try1"); + if (z == 1) + { + return; + } + System.out.println("Try2"); + } catch (Exception ex) + { + System.out.println("Exception"); + if (z == 1) + { + return; + } + } finally + { + System.out.println("Finally"); + if (z == 1) + { + return; + } + System.out.println("Finally2"); + } + int x = 1; + System.out.println("Error: " + e); + x = x + 1; + } finally + { + int y = 12; + System.out.println("Finally"); + y = y + 1; + } + z = z + 1; + + try + { + System.out.println("Try1"); + if (z == 1) + { + return; + } + System.out.println("Try2"); + } catch (Exception ex) + { + System.out.println("Exception"); + if (z == 1) + { + return; + } + } finally + { + System.out.println("Finally"); + if (z == 1) + { + return; + } + System.out.println("Finally2"); + } + + z = z + 2; + } +} diff --git a/java/ql/test/library-tests/successors/TestFinallyBreakContinue/TestFinallyBreakContinue.java b/java/ql/test/library-tests/successors/TestFinallyBreakContinue/TestFinallyBreakContinue.java index 480713f60d07..04b232a4ba34 100644 --- a/java/ql/test/library-tests/successors/TestFinallyBreakContinue/TestFinallyBreakContinue.java +++ b/java/ql/test/library-tests/successors/TestFinallyBreakContinue/TestFinallyBreakContinue.java @@ -1,108 +1,108 @@ -package successors; - -public class TestFinallyBreakContinue { - public void f() - { - int x = 1; - a: - for (;;) - { - try - { - if (x == 1) - { - break; - } else - { - continue; - } - } catch (Exception e) - { - if (x == 1) - { - break; - } else - { - continue; - } - } finally - { - System.out.println("finally"); - } - } - - while (true) - { - try - { - try - { - if (x == 1) - { - break; - } else - { - continue; - } - } catch (Exception e) - { - if (x == 1) - { - break; - } else - { - continue; - } - } finally - { - System.out.println("finally"); - } - } catch (Exception e) - { - System.out.println("Exception"); - } finally - { - System.out.println("finally"); - } - } - - b: - do - { - try - { - for (int i : new int[20]) - { - try - { - if (x == 1) - { - break; - } else - { - continue; - } - } catch (Exception e) - { - if (x == 1) - { - break b; - } else - { - continue b; - } - } finally - { - System.out.println("finally"); - } - } - } catch (Exception e) - { - System.out.println("Exception"); - } finally - { - System.out.println("finally"); - } - } while (true); - } -} +package successors; + +public class TestFinallyBreakContinue { + public void f() + { + int x = 1; + a: + for (;;) + { + try + { + if (x == 1) + { + break; + } else + { + continue; + } + } catch (Exception e) + { + if (x == 1) + { + break; + } else + { + continue; + } + } finally + { + System.out.println("finally"); + } + } + + while (true) + { + try + { + try + { + if (x == 1) + { + break; + } else + { + continue; + } + } catch (Exception e) + { + if (x == 1) + { + break; + } else + { + continue; + } + } finally + { + System.out.println("finally"); + } + } catch (Exception e) + { + System.out.println("Exception"); + } finally + { + System.out.println("finally"); + } + } + + b: + do + { + try + { + for (int i : new int[20]) + { + try + { + if (x == 1) + { + break; + } else + { + continue; + } + } catch (Exception e) + { + if (x == 1) + { + break b; + } else + { + continue b; + } + } finally + { + System.out.println("finally"); + } + } + } catch (Exception e) + { + System.out.println("Exception"); + } finally + { + System.out.println("finally"); + } + } while (true); + } +} diff --git a/java/ql/test/library-tests/successors/TestLoopBranch/TestLoopBranch.java b/java/ql/test/library-tests/successors/TestLoopBranch/TestLoopBranch.java index 60906547952c..058a9d40275f 100644 --- a/java/ql/test/library-tests/successors/TestLoopBranch/TestLoopBranch.java +++ b/java/ql/test/library-tests/successors/TestLoopBranch/TestLoopBranch.java @@ -1,120 +1,120 @@ -package successors; - -public class TestLoopBranch { - int xx = 12; - int yy = 13; - - public void f() - { - int x = 1; - int y = 2; - System.out.println("foo"); - - do - { - System.out.println("bar"); - System.out.println("foobar"); - } while (x == 2); - - { - System.out.println("shazam"); - System.out.println("boogie"); - } - - while (x == 1) - { - System.out.println("wonderland"); - System.out.println("shodan"); - x = x + 1; - } - - for (int i = 0; i < 10; i++) - { - System.out.println("rapture"); - y = x - 2; - } - - ; - ; - - for (int j : new int[20]) - { - System.out.println("Zero : " + j); - j = j + x; - } - - if (y == -1) - { - System.out.println("i squared"); - } - - if (x == 42) - { - System.out.println("rat"); - x = 6 * 9; - } else - { - System.out.println("arr"); - x = y * x; - return; - } - - switch (x) - { - case 1: - x = x + 1; - y = y + 1; - case 2: - x = x + 2; - y = y + 2; - break; - case 3: - case 4: - x = x + 3; - y = y + 4; - break; - case 5: - case 6: - x = x + 5; - y = y + 6; - default: - x = y; - y = x; - } - - //no default - switch(x) - { - case 1: - x = 1; - break; - case 2: - x = 2; - break; - } - - Comparable b = new Comparable() { - @Override - public int compareTo(String o) - { - return 0; - } - }; - b.compareTo("Foo"); - - x = x + y; - return; - } - - public TestLoopBranch() - { - xx = 33; - yy = 44; - } - - public TestLoopBranch(int i) - { - xx = i; - yy = i; - } +package successors; + +public class TestLoopBranch { + int xx = 12; + int yy = 13; + + public void f() + { + int x = 1; + int y = 2; + System.out.println("foo"); + + do + { + System.out.println("bar"); + System.out.println("foobar"); + } while (x == 2); + + { + System.out.println("shazam"); + System.out.println("boogie"); + } + + while (x == 1) + { + System.out.println("wonderland"); + System.out.println("shodan"); + x = x + 1; + } + + for (int i = 0; i < 10; i++) + { + System.out.println("rapture"); + y = x - 2; + } + + ; + ; + + for (int j : new int[20]) + { + System.out.println("Zero : " + j); + j = j + x; + } + + if (y == -1) + { + System.out.println("i squared"); + } + + if (x == 42) + { + System.out.println("rat"); + x = 6 * 9; + } else + { + System.out.println("arr"); + x = y * x; + return; + } + + switch (x) + { + case 1: + x = x + 1; + y = y + 1; + case 2: + x = x + 2; + y = y + 2; + break; + case 3: + case 4: + x = x + 3; + y = y + 4; + break; + case 5: + case 6: + x = x + 5; + y = y + 6; + default: + x = y; + y = x; + } + + //no default + switch(x) + { + case 1: + x = 1; + break; + case 2: + x = 2; + break; + } + + Comparable b = new Comparable() { + @Override + public int compareTo(String o) + { + return 0; + } + }; + b.compareTo("Foo"); + + x = x + y; + return; + } + + public TestLoopBranch() + { + xx = 33; + yy = 44; + } + + public TestLoopBranch(int i) + { + xx = i; + yy = i; + } } \ No newline at end of file diff --git a/java/ql/test/library-tests/successors/TestThrow/TestThrow.java b/java/ql/test/library-tests/successors/TestThrow/TestThrow.java index 78df2a3595fe..58098fc65058 100644 --- a/java/ql/test/library-tests/successors/TestThrow/TestThrow.java +++ b/java/ql/test/library-tests/successors/TestThrow/TestThrow.java @@ -1,135 +1,135 @@ -package successors; - -import java.io.IOException; -import java.security.InvalidParameterException; - -public class TestThrow { - private TestThrow() throws IOException - { - } - - private void thrower() throws InvalidParameterException - { - } - - public void f() throws Exception - { - int z = 0; - try - { - throw new RuntimeException(); - } catch (RuntimeException e) - { - z = 1; - } catch (Exception e) - { - z = 2; - } - - z = -1; - - try - { - if (z == 1) - { - throw new RuntimeException(); - } else if (z == 2) - { - throw new Exception(); - } else if (z == 3) - { - new TestThrow(); - } else - { - thrower(); - } - } catch (RuntimeException e) - { - z = 1; - } finally - { - z = 2; - } - - z = -1; - - try - { - if (z == 1) - { - throw new Exception(); - } - else if (z == 2) - { - new TestThrow(); - } else - { - thrower(); - } - } catch (RuntimeException e) - { - z = 1; - } - - z = -1; - - try - { - if (z == 1) - throw new Exception(); - } finally - { - z = 1; - } - - try - { - try - { - if (z == 1) - { - throw new Exception(); - } else if (z == 2) - { - throw new RuntimeException(); - } else - { - throw new IOException("Foo bar", null); - } - } catch (RuntimeException e) - { - z = 1; - } - try - { - z = -2; - } finally - { - if (z == 1) - { - throw new Exception(); - } else if (z == 2) - { - throw new RuntimeException(); - } else if (z == 3) - { - throw new IOException("Foo bar", null); - } - } - } catch (IOException e) - { - z = 2; - } - finally - { - z = 3; - } - - if (z == 1) - { - throw new Exception(); - } - - z = -1; - } -} +package successors; + +import java.io.IOException; +import java.security.InvalidParameterException; + +public class TestThrow { + private TestThrow() throws IOException + { + } + + private void thrower() throws InvalidParameterException + { + } + + public void f() throws Exception + { + int z = 0; + try + { + throw new RuntimeException(); + } catch (RuntimeException e) + { + z = 1; + } catch (Exception e) + { + z = 2; + } + + z = -1; + + try + { + if (z == 1) + { + throw new RuntimeException(); + } else if (z == 2) + { + throw new Exception(); + } else if (z == 3) + { + new TestThrow(); + } else + { + thrower(); + } + } catch (RuntimeException e) + { + z = 1; + } finally + { + z = 2; + } + + z = -1; + + try + { + if (z == 1) + { + throw new Exception(); + } + else if (z == 2) + { + new TestThrow(); + } else + { + thrower(); + } + } catch (RuntimeException e) + { + z = 1; + } + + z = -1; + + try + { + if (z == 1) + throw new Exception(); + } finally + { + z = 1; + } + + try + { + try + { + if (z == 1) + { + throw new Exception(); + } else if (z == 2) + { + throw new RuntimeException(); + } else + { + throw new IOException("Foo bar", null); + } + } catch (RuntimeException e) + { + z = 1; + } + try + { + z = -2; + } finally + { + if (z == 1) + { + throw new Exception(); + } else if (z == 2) + { + throw new RuntimeException(); + } else if (z == 3) + { + throw new IOException("Foo bar", null); + } + } + } catch (IOException e) + { + z = 2; + } + finally + { + z = 3; + } + + if (z == 1) + { + throw new Exception(); + } + + z = -1; + } +} diff --git a/java/ql/test/library-tests/successors/TestTryCatch/TestTryCatch.java b/java/ql/test/library-tests/successors/TestTryCatch/TestTryCatch.java index 38b320ae8e12..09466051023d 100644 --- a/java/ql/test/library-tests/successors/TestTryCatch/TestTryCatch.java +++ b/java/ql/test/library-tests/successors/TestTryCatch/TestTryCatch.java @@ -1,44 +1,44 @@ -package successors; - -public class TestTryCatch { - public void f() - { - try - { - System.out.println("Foo"); - int y = 12 + 3; - System.out.println("Bar"); - y = y + 1; - } catch (Exception e) - { - int x = 1; - System.out.println("Error: " + e); - x = x + 1; - return; - } finally - { - int y = 12; - System.out.println("Finally"); - y = y + 1; - } - int z = 12; - z = z + 1; - - for (int q = 0; q < 10; q++) - { - try - { - System.out.println("Foo"); - int y = 12 + 3; - System.out.println("Bar"); - y = y + 1; - } catch (RuntimeException e) - { - int x = 1; - System.out.println("Error: " + e); - x = x + 1; - } - } - z = z + 2; - } -} +package successors; + +public class TestTryCatch { + public void f() + { + try + { + System.out.println("Foo"); + int y = 12 + 3; + System.out.println("Bar"); + y = y + 1; + } catch (Exception e) + { + int x = 1; + System.out.println("Error: " + e); + x = x + 1; + return; + } finally + { + int y = 12; + System.out.println("Finally"); + y = y + 1; + } + int z = 12; + z = z + 1; + + for (int q = 0; q < 10; q++) + { + try + { + System.out.println("Foo"); + int y = 12 + 3; + System.out.println("Bar"); + y = y + 1; + } catch (RuntimeException e) + { + int x = 1; + System.out.println("Error: " + e); + x = x + 1; + } + } + z = z + 2; + } +} diff --git a/java/ql/test/query-tests/AlertSuppression/.gitattributes b/java/ql/test/query-tests/AlertSuppression/.gitattributes new file mode 100644 index 000000000000..1dd1b58fd4a3 --- /dev/null +++ b/java/ql/test/query-tests/AlertSuppression/.gitattributes @@ -0,0 +1 @@ +TestWindows.java eol=crlf diff --git a/java/ql/test/query-tests/AlertSuppression/TestWindows.java b/java/ql/test/query-tests/AlertSuppression/TestWindows.java index da2539a30832..07845e3c0a5e 100644 --- a/java/ql/test/query-tests/AlertSuppression/TestWindows.java +++ b/java/ql/test/query-tests/AlertSuppression/TestWindows.java @@ -1,28 +1,28 @@ -class TestWindows {} // lgtm -// lgtm[java/confusing-method-name] -// lgtm[java/confusing-method-name, java/non-short-circuit-evaluation] -// lgtm[@tag:exceptions] -// lgtm[@tag:exceptions,java/confusing-method-name] -// lgtm[@expires:2017-06-11] -// lgtm[java/confusing-method-name] does not seem confusing despite alert by lgtm -// lgtm: blah blah -// lgtm blah blah #falsepositive -//lgtm [java/confusing-method-name] -/* lgtm */ -// lgtm[] -// lgtmfoo -//lgtm -// lgtm -// lgtm [java/confusing-method-name] -// foolgtm[java/confusing-method-name] -// foolgtm -// foo; lgtm -// foo; lgtm[java/confusing-method-name] -// foo lgtm -// foo lgtm[java/confusing-method-name] -// foo lgtm bar -// foo lgtm[java/confusing-method-name] bar -// LGTM! -// LGTM[java/confusing-method-name] -//lgtm[java/confusing-method-name] and lgtm[java/non-short-circuit-evaluation] -//lgtm[java/confusing-method-name]; lgtm +class TestWindows {} // lgtm +// lgtm[java/confusing-method-name] +// lgtm[java/confusing-method-name, java/non-short-circuit-evaluation] +// lgtm[@tag:exceptions] +// lgtm[@tag:exceptions,java/confusing-method-name] +// lgtm[@expires:2017-06-11] +// lgtm[java/confusing-method-name] does not seem confusing despite alert by lgtm +// lgtm: blah blah +// lgtm blah blah #falsepositive +//lgtm [java/confusing-method-name] +/* lgtm */ +// lgtm[] +// lgtmfoo +//lgtm +// lgtm +// lgtm [java/confusing-method-name] +// foolgtm[java/confusing-method-name] +// foolgtm +// foo; lgtm +// foo; lgtm[java/confusing-method-name] +// foo lgtm +// foo lgtm[java/confusing-method-name] +// foo lgtm bar +// foo lgtm[java/confusing-method-name] bar +// LGTM! +// LGTM[java/confusing-method-name] +//lgtm[java/confusing-method-name] and lgtm[java/non-short-circuit-evaluation] +//lgtm[java/confusing-method-name]; lgtm diff --git a/javascript/ql/src/plugin.xml b/javascript/ql/src/plugin.xml index 206533511dcb..19bcfc22249f 100644 --- a/javascript/ql/src/plugin.xml +++ b/javascript/ql/src/plugin.xml @@ -1,16 +1,16 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + From 1f36f5552fe908ac774b7c684639208d94713e03 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 21 Sep 2018 07:50:03 -0700 Subject: [PATCH 095/110] Normalize all text files to LF Use `* text=auto eol=lf` --- .editorconfig | 4 +- .gitattributes | 73 ++++++++++--------- cpp/config/suites/security/cwe-120 | 26 +++---- cpp/config/suites/security/cwe-121 | 6 +- cpp/config/suites/security/cwe-131 | 14 ++-- cpp/config/suites/security/cwe-134 | 26 +++---- cpp/ql/test/.project | 24 +++--- cpp/ql/test/library-tests/exprs_cast/options | 2 +- .../library-tests/typename/typename.expected | 8 +- .../library-tests/types/__wchar_t/options | 2 +- .../types/integral_types_ms/options | 2 +- .../types/wchar_t_typedef/options | 2 +- .../AlertSuppression/.gitattributes | 2 +- .../Unused Entities/UnusedIncludes/g | 2 +- .../test/query-tests/Critical/NewFree/options | 2 +- csharp/ql/test/.project | 24 +++--- .../library-tests/assemblies/Assembly1.cs_ | 40 +++++----- .../library-tests/assemblies/Assembly2.cs_ | 46 ++++++------ .../generics/Generics20.expected | 2 +- .../library-tests/tokens/Tokens1.expected | 6 +- .../ReadOnlyContainer.expected | 20 ++--- .../src/.settings/org.eclipse.jdt.core.prefs | 14 ++-- .../library-tests/comments/.gitattributes | 2 +- .../AlertSuppression/.gitattributes | 2 +- .../test/library-tests/Lines/.gitattributes | 4 +- 25 files changed, 178 insertions(+), 177 deletions(-) diff --git a/.editorconfig b/.editorconfig index 268bd5a7ecbc..270106b1facf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*] -end_of_line = lf +[*] +end_of_line = lf diff --git a/.gitattributes b/.gitattributes index bd6bfa96bcc8..779c409b1c69 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,40 +1,41 @@ -# The following file types will be normalized to LF line endings in the Git -# database, and will keep those LF line endings in the working tree even on -# Windows. Any other files will have whatever line endings they had when they -# were committed. If you add new entries below, you should renormalize the -# affected files by running the following from the root of this repo (requires -# Git 2.16 or greater): +# Text files will be normalized to LF line endings in the Git database, and will keep those LF line +# endings in the working tree even on Windows. If you make changes below, you should renormalize the +# affected files by running the following from the root of this repo (requires Git 2.16 or greater): # # git add --renormalize . # git status [just to show what files were renormalized] # git commit -m "Normalize line endings" -# -# Also, please update .editorconfig to handle any new entries as well. -*.ql eol=lf -*.qll eol=lf -*.qlref eol=lf -*.dbscheme eol=lf -*.qhelp eol=lf -*.html eol=lf -*.htm eol=lf -*.xhtml eol=lf -*.xhtm eol=lf -*.js eol=lf -*.mjs eol=lf -*.ts eol=lf -*.json eol=lf -*.yml eol=lf -*.yaml eol=lf -*.c eol=lf -*.cpp eol=lf -*.h eol=lf -*.hpp eol=lf -*.md eol=lf -*.stats eol=lf -*.xml eol=lf -*.sh eol=lf -*.pl eol=lf -*.java eol=lf -*.cs eol=lf -*.py eol=lf -*.lua eol=lf + +# Anything Git auto-detects as text gets normalized and checked out as LF +* text=auto eol=lf + +# Explicitly set a bunch of known extensions to text, in case auto detection gets confused. +*.ql text +*.qll text +*.qlref text +*.dbscheme text +*.qhelp text +*.html text +*.htm text +*.xhtml text +*.xhtm text +*.js text +*.mjs text +*.ts text +*.json text +*.yml text +*.yaml text +*.c text +*.cpp text +*.h text +*.hpp text +*.md text +*.stats text +*.xml text +*.sh text +*.pl text +*.java text +*.cs text +*.py text +*.lua text +*.expected text diff --git a/cpp/config/suites/security/cwe-120 b/cpp/config/suites/security/cwe-120 index 0343041d79e1..483958868eb8 100644 --- a/cpp/config/suites/security/cwe-120 +++ b/cpp/config/suites/security/cwe-120 @@ -1,13 +1,13 @@ -# CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') -+ semmlecode-cpp-queries/Security/CWE/CWE-120/UnboundedWrite.ql: /CWE/CWE-120 - @name Unbounded write (CWE-120) -+ semmlecode-cpp-queries/Security/CWE/CWE-120/BadlyBoundedWrite.ql: /CWE/CWE-120 - @name Badly bounded write (CWE-120) -+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWrite.ql: /CWE/CWE-120 - @name Potentially overrunning write (CWE-120) -+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWriteFloat.ql: /CWE/CWE-120 - @name Potentially overrunning write with float to string conversion (CWE-120) -+ semmlecode-cpp-queries/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql: /CWE/CWE-120 - @name Array offset used before range check (CWE-120) -+ semmlecode-cpp-queries/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql: /CWE/CWE-120 - @name Potentially unsafe use of strcat (CWE-120) +# CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') ++ semmlecode-cpp-queries/Security/CWE/CWE-120/UnboundedWrite.ql: /CWE/CWE-120 + @name Unbounded write (CWE-120) ++ semmlecode-cpp-queries/Security/CWE/CWE-120/BadlyBoundedWrite.ql: /CWE/CWE-120 + @name Badly bounded write (CWE-120) ++ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWrite.ql: /CWE/CWE-120 + @name Potentially overrunning write (CWE-120) ++ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWriteFloat.ql: /CWE/CWE-120 + @name Potentially overrunning write with float to string conversion (CWE-120) ++ semmlecode-cpp-queries/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql: /CWE/CWE-120 + @name Array offset used before range check (CWE-120) ++ semmlecode-cpp-queries/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql: /CWE/CWE-120 + @name Potentially unsafe use of strcat (CWE-120) diff --git a/cpp/config/suites/security/cwe-121 b/cpp/config/suites/security/cwe-121 index c9f922d8c55b..66c4da6b08db 100644 --- a/cpp/config/suites/security/cwe-121 +++ b/cpp/config/suites/security/cwe-121 @@ -1,3 +1,3 @@ -# CWE-121: Stack-based Buffer Overflow -+ semmlecode-cpp-queries/Security/CWE/CWE-121/UnterminatedVarargsCall.ql: /CWE/CWE-121 - @name Unterminated variadic call (CWE-121) +# CWE-121: Stack-based Buffer Overflow ++ semmlecode-cpp-queries/Security/CWE/CWE-121/UnterminatedVarargsCall.ql: /CWE/CWE-121 + @name Unterminated variadic call (CWE-121) diff --git a/cpp/config/suites/security/cwe-131 b/cpp/config/suites/security/cwe-131 index ae6751459024..5bbdf081b86d 100644 --- a/cpp/config/suites/security/cwe-131 +++ b/cpp/config/suites/security/cwe-131 @@ -1,7 +1,7 @@ -# CWE-131: Incorrect Calculation of Buffer Size -+ semmlecode-cpp-queries/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql: /CWE/CWE-131 - @name No space for zero terminator (CWE-131) -+ semmlecode-cpp-queries/Critical/SizeCheck.ql: /CWE/CWE-131 - @name Not enough memory allocated for pointer type (CWE-131) -+ semmlecode-cpp-queries/Critical/SizeCheck2.ql: /CWE/CWE-131 - @name Not enough memory allocated for array of pointer type (CWE-131) +# CWE-131: Incorrect Calculation of Buffer Size ++ semmlecode-cpp-queries/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql: /CWE/CWE-131 + @name No space for zero terminator (CWE-131) ++ semmlecode-cpp-queries/Critical/SizeCheck.ql: /CWE/CWE-131 + @name Not enough memory allocated for pointer type (CWE-131) ++ semmlecode-cpp-queries/Critical/SizeCheck2.ql: /CWE/CWE-131 + @name Not enough memory allocated for array of pointer type (CWE-131) diff --git a/cpp/config/suites/security/cwe-134 b/cpp/config/suites/security/cwe-134 index b6c1d5411a55..060ebbfa70be 100644 --- a/cpp/config/suites/security/cwe-134 +++ b/cpp/config/suites/security/cwe-134 @@ -1,13 +1,13 @@ -# CWE-134: Uncontrolled Format String -+ semmlecode-cpp-queries/Likely Bugs/Format/NonConstantFormat.ql: /CWE/CWE-134 - @name Non-constant format string (CWE-134) -# This one runs out of memory. See ODASA-608. -#+ semmlecode-cpp-queries/PointsTo/TaintedFormatStrings.ql: /CWE/CWE-134 -+ semmlecode-cpp-queries/Likely Bugs/Format/WrongNumberOfFormatArguments.ql: /CWE/CWE-134 - @name Wrong number of arguments to formatting function (CWE-134) -+ semmlecode-cpp-queries/Likely Bugs/Format/WrongTypeFormatArguments.ql: /CWE/CWE-134 - @name Wrong type of arguments to formatting function (CWE-134) -+ semmlecode-cpp-queries/Security/CWE/CWE-134/UncontrolledFormatString.ql: /CWE/CWE-134 - @name Uncontrolled format string (CWE-134) -+ semmlecode-cpp-queries/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql: /CWE/CWE-134 - @name Uncontrolled format string (through global variable) (CWE-134) +# CWE-134: Uncontrolled Format String ++ semmlecode-cpp-queries/Likely Bugs/Format/NonConstantFormat.ql: /CWE/CWE-134 + @name Non-constant format string (CWE-134) +# This one runs out of memory. See ODASA-608. +#+ semmlecode-cpp-queries/PointsTo/TaintedFormatStrings.ql: /CWE/CWE-134 ++ semmlecode-cpp-queries/Likely Bugs/Format/WrongNumberOfFormatArguments.ql: /CWE/CWE-134 + @name Wrong number of arguments to formatting function (CWE-134) ++ semmlecode-cpp-queries/Likely Bugs/Format/WrongTypeFormatArguments.ql: /CWE/CWE-134 + @name Wrong type of arguments to formatting function (CWE-134) ++ semmlecode-cpp-queries/Security/CWE/CWE-134/UncontrolledFormatString.ql: /CWE/CWE-134 + @name Uncontrolled format string (CWE-134) ++ semmlecode-cpp-queries/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql: /CWE/CWE-134 + @name Uncontrolled format string (through global variable) (CWE-134) diff --git a/cpp/ql/test/.project b/cpp/ql/test/.project index 2dbe9983f8c8..bff15d701e3c 100644 --- a/cpp/ql/test/.project +++ b/cpp/ql/test/.project @@ -1,12 +1,12 @@ - - - semmlecode-cpp-tests - - - - - - - com.semmle.plugin.qdt.core.qlnature - - + + + semmlecode-cpp-tests + + + + + + + com.semmle.plugin.qdt.core.qlnature + + diff --git a/cpp/ql/test/library-tests/exprs_cast/options b/cpp/ql/test/library-tests/exprs_cast/options index 6158faf8d2f3..d8db22a4bcb8 100644 --- a/cpp/ql/test/library-tests/exprs_cast/options +++ b/cpp/ql/test/library-tests/exprs_cast/options @@ -1 +1 @@ -semmle-extractor-options: --edg --target --edg win64 +semmle-extractor-options: --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/typename/typename.expected b/cpp/ql/test/library-tests/typename/typename.expected index 09bff6e139e5..31a76b90ab26 100644 --- a/cpp/ql/test/library-tests/typename/typename.expected +++ b/cpp/ql/test/library-tests/typename/typename.expected @@ -1,4 +1,4 @@ -| file://:0:0:0:0 | T | -| file://:0:0:0:0 | int | -| file://:0:0:0:0 | myClass | -| file://:0:0:0:0 | short | +| file://:0:0:0:0 | T | +| file://:0:0:0:0 | int | +| file://:0:0:0:0 | myClass | +| file://:0:0:0:0 | short | diff --git a/cpp/ql/test/library-tests/types/__wchar_t/options b/cpp/ql/test/library-tests/types/__wchar_t/options index 8ee3fcb91630..c3fc566fda21 100644 --- a/cpp/ql/test/library-tests/types/__wchar_t/options +++ b/cpp/ql/test/library-tests/types/__wchar_t/options @@ -1 +1 @@ -semmle-extractor-options: --microsoft +semmle-extractor-options: --microsoft diff --git a/cpp/ql/test/library-tests/types/integral_types_ms/options b/cpp/ql/test/library-tests/types/integral_types_ms/options index 3eadeb8d45a0..668c53efd7db 100644 --- a/cpp/ql/test/library-tests/types/integral_types_ms/options +++ b/cpp/ql/test/library-tests/types/integral_types_ms/options @@ -1 +1 @@ -semmle-extractor-options: --microsoft --edg --target --edg win64 +semmle-extractor-options: --microsoft --edg --target --edg win64 diff --git a/cpp/ql/test/library-tests/types/wchar_t_typedef/options b/cpp/ql/test/library-tests/types/wchar_t_typedef/options index b1ef636d3e90..979872636a4b 100644 --- a/cpp/ql/test/library-tests/types/wchar_t_typedef/options +++ b/cpp/ql/test/library-tests/types/wchar_t_typedef/options @@ -1 +1 @@ -semmle-extractor-options: --microsoft --edg --target --edg win32 +semmle-extractor-options: --microsoft --edg --target --edg win32 diff --git a/cpp/ql/test/query-tests/AlertSuppression/.gitattributes b/cpp/ql/test/query-tests/AlertSuppression/.gitattributes index 03feb5510a31..ba9355dd6018 100644 --- a/cpp/ql/test/query-tests/AlertSuppression/.gitattributes +++ b/cpp/ql/test/query-tests/AlertSuppression/.gitattributes @@ -1 +1 @@ -tstWindows.c eol=crlf +tstWindows.c eol=crlf diff --git a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/g b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/g index c3076bdfb829..7d808bf9b83e 100644 --- a/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/g +++ b/cpp/ql/test/query-tests/Best Practices/Unused Entities/UnusedIncludes/g @@ -1 +1 @@ -// g +// g diff --git a/cpp/ql/test/query-tests/Critical/NewFree/options b/cpp/ql/test/query-tests/Critical/NewFree/options index 8ee3fcb91630..c3fc566fda21 100644 --- a/cpp/ql/test/query-tests/Critical/NewFree/options +++ b/cpp/ql/test/query-tests/Critical/NewFree/options @@ -1 +1 @@ -semmle-extractor-options: --microsoft +semmle-extractor-options: --microsoft diff --git a/csharp/ql/test/.project b/csharp/ql/test/.project index 1fa51e991e3d..941c2e784d48 100644 --- a/csharp/ql/test/.project +++ b/csharp/ql/test/.project @@ -1,12 +1,12 @@ - - - semmlecode-csharp-tests - - - - - - - com.semmle.plugin.qdt.core.qlnature - - + + + semmlecode-csharp-tests + + + + + + + com.semmle.plugin.qdt.core.qlnature + + diff --git a/csharp/ql/test/library-tests/assemblies/Assembly1.cs_ b/csharp/ql/test/library-tests/assemblies/Assembly1.cs_ index 1e3f1b8c64e9..e8484c4c377d 100644 --- a/csharp/ql/test/library-tests/assemblies/Assembly1.cs_ +++ b/csharp/ql/test/library-tests/assemblies/Assembly1.cs_ @@ -1,20 +1,20 @@ -using System; - -namespace Assembly2 -{ - public delegate int del2(int x); - - public class Class2 : Attribute - { - } - - public class Class3 : Attribute - { - public class Class4 - { - } - public class Class5 - { - } - } -} +using System; + +namespace Assembly2 +{ + public delegate int del2(int x); + + public class Class2 : Attribute + { + } + + public class Class3 : Attribute + { + public class Class4 + { + } + public class Class5 + { + } + } +} diff --git a/csharp/ql/test/library-tests/assemblies/Assembly2.cs_ b/csharp/ql/test/library-tests/assemblies/Assembly2.cs_ index 4606901a5def..4d04c87acf66 100644 --- a/csharp/ql/test/library-tests/assemblies/Assembly2.cs_ +++ b/csharp/ql/test/library-tests/assemblies/Assembly2.cs_ @@ -1,23 +1,23 @@ -using System; -using Assembly2; - -namespace Assembly1 -{ - [Class3()] - public delegate Class2 del1(Class3 c3); - - [Class3()] - public class Class1 - { - [Class2()] - static public Class2 a; - static public Class2 b() { return new Class2(); } - [Class3()] - static public Class3.Class4 c(Class3 x) { return new Class3.Class4(); } - [Class2()] - static public int d() { return 1; } - static public del2 e() { return f; } - static public int f(int x) { return x; } - static public Class3.Class5 g(Class3.Class5 x) { return x; } - } -} +using System; +using Assembly2; + +namespace Assembly1 +{ + [Class3()] + public delegate Class2 del1(Class3 c3); + + [Class3()] + public class Class1 + { + [Class2()] + static public Class2 a; + static public Class2 b() { return new Class2(); } + [Class3()] + static public Class3.Class4 c(Class3 x) { return new Class3.Class4(); } + [Class2()] + static public int d() { return 1; } + static public del2 e() { return f; } + static public int f(int x) { return x; } + static public Class3.Class5 g(Class3.Class5 x) { return x; } + } +} diff --git a/csharp/ql/test/library-tests/generics/Generics20.expected b/csharp/ql/test/library-tests/generics/Generics20.expected index 78af767cb726..2a4f078a25fc 100755 --- a/csharp/ql/test/library-tests/generics/Generics20.expected +++ b/csharp/ql/test/library-tests/generics/Generics20.expected @@ -1 +1 @@ -| 1 | +| 1 | diff --git a/csharp/ql/test/library-tests/tokens/Tokens1.expected b/csharp/ql/test/library-tests/tokens/Tokens1.expected index 3956fb2ce935..0ed42634419c 100644 --- a/csharp/ql/test/library-tests/tokens/Tokens1.expected +++ b/csharp/ql/test/library-tests/tokens/Tokens1.expected @@ -1,3 +1,3 @@ -| tokens.cs:5:15:5:17 | I1 | -| tokens.cs:10:12:10:14 | S1 | -| tokens.cs:14:11:14:13 | C1 | +| tokens.cs:5:15:5:17 | I1 | +| tokens.cs:10:12:10:14 | S1 | +| tokens.cs:14:11:14:13 | C1 | diff --git a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected index 98b43cffc28e..39e5f5e96284 100755 --- a/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected +++ b/csharp/ql/test/query-tests/ReadOnlyContainer/ReadOnlyContainer.expected @@ -1,11 +1,11 @@ -| ReadOnlyContainer.cs:9:16:9:17 | v1 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:13:13:13:14 | v2 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:30:16:30:17 | v3 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:55:13:55:14 | v4 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:76:13:76:14 | v5 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:82:13:82:14 | v6 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:88:13:88:14 | v7 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:91:13:91:14 | v8 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:96:13:96:14 | v9 | The contents of this container are never initialized. | -| ReadOnlyContainer.cs:99:13:99:15 | v10 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:9:16:9:17 | v1 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:13:13:13:14 | v2 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:30:16:30:17 | v3 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:55:13:55:14 | v4 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:76:13:76:14 | v5 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:82:13:82:14 | v6 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:88:13:88:14 | v7 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:91:13:91:14 | v8 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:96:13:96:14 | v9 | The contents of this container are never initialized. | +| ReadOnlyContainer.cs:99:13:99:15 | v10 | The contents of this container are never initialized. | | ReadOnlyContainer.cs:121:13:121:15 | v11 | The contents of this container are never initialized. | \ No newline at end of file diff --git a/java/ql/src/.settings/org.eclipse.jdt.core.prefs b/java/ql/src/.settings/org.eclipse.jdt.core.prefs index 860376bf985f..b36152aea390 100644 --- a/java/ql/src/.settings/org.eclipse.jdt.core.prefs +++ b/java/ql/src/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,7 @@ -#Tue Nov 04 11:42:37 GMT 2008 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 +#Tue Nov 04 11:42:37 GMT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/java/ql/test/library-tests/comments/.gitattributes b/java/ql/test/library-tests/comments/.gitattributes index 1dd1b58fd4a3..7ed66a396cfd 100644 --- a/java/ql/test/library-tests/comments/.gitattributes +++ b/java/ql/test/library-tests/comments/.gitattributes @@ -1 +1 @@ -TestWindows.java eol=crlf +TestWindows.java eol=crlf diff --git a/java/ql/test/query-tests/AlertSuppression/.gitattributes b/java/ql/test/query-tests/AlertSuppression/.gitattributes index 1dd1b58fd4a3..7ed66a396cfd 100644 --- a/java/ql/test/query-tests/AlertSuppression/.gitattributes +++ b/java/ql/test/query-tests/AlertSuppression/.gitattributes @@ -1 +1 @@ -TestWindows.java eol=crlf +TestWindows.java eol=crlf diff --git a/javascript/ql/test/library-tests/Lines/.gitattributes b/javascript/ql/test/library-tests/Lines/.gitattributes index 4d67c9960a4f..3ca67868c632 100644 --- a/javascript/ql/test/library-tests/Lines/.gitattributes +++ b/javascript/ql/test/library-tests/Lines/.gitattributes @@ -1,2 +1,2 @@ -# This file intentionally contains a mix of different line endings -tst1.js -text +# This file intentionally contains a mix of different line endings +tst1.js -text From 396d7ea928b737fa6829d45e5d2d45717606613d Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 21 Sep 2018 11:36:28 -0700 Subject: [PATCH 096/110] Mark several known binary extensions as `-text` --- .gitattributes | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitattributes b/.gitattributes index 779c409b1c69..626c5a297c83 100644 --- a/.gitattributes +++ b/.gitattributes @@ -39,3 +39,10 @@ *.py text *.lua text *.expected text + +# Explicitly set a bunch of known extensions to binary, because Git < 2.10 will treat +# `* text=auto eol=lf` as `* text eol=lf` +*.png -text +*.jpg -text +*.jpeg -text +*.gif -text From 2b5d15082965ddfc5a79456cbb3aaad8a9f57b7d Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 24 Sep 2018 14:29:51 +0200 Subject: [PATCH 097/110] C++: Test for IntMultToLong on char-typed numbers --- .../Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c | 4 ++++ .../Arithmetic/IntMultToLong/IntMultToLong.expected | 1 + 2 files changed, 5 insertions(+) diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c index 603fd50b7747..4afd40f5839b 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c @@ -88,3 +88,7 @@ void use_printf(float f, double d) // ^ there's a float -> double varargs promotion here, but it's unlikely that the author anticipates requiring a double printf("%f", d * d); // safe } + +size_t three_chars(unsigned char a, unsigned char b, unsigned char c) { + return a * b * c; // at most 16581375 [FALSE POSITIVE] +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected index 4a5edf213790..25cef782aeec 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected @@ -7,3 +7,4 @@ | IntMultToLong.c:61:23:61:33 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. | | IntMultToLong.c:63:23:63:40 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. | | IntMultToLong.c:75:9:75:13 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. | +| IntMultToLong.c:93:12:93:20 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. | From 4d2e4c53f155ddff378df6e3235b3772d2c9faae Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 24 Sep 2018 14:30:44 +0200 Subject: [PATCH 098/110] C++: Suppress IntMultToLong alert on char --- cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql | 2 ++ .../Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c | 2 +- .../Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index ac2bed3d6f99..4ab00c2d2804 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -21,6 +21,7 @@ import semmle.code.cpp.controlflow.SSA /** * Holds if `e` is either: * - a constant + * - a char-typed expression, meaning it's a small number * - an array access to an array of constants * - flows from one of the above * In these cases the value of `e` is likely to be small and @@ -28,6 +29,7 @@ import semmle.code.cpp.controlflow.SSA */ predicate effectivelyConstant(Expr e) { e.isConstant() or + e.getType().getSize() <= 1 or e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst() or exists(SsaDefinition def, Variable v | def.getAUse(v) = e and diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c index 4afd40f5839b..76ab0e83c599 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c @@ -90,5 +90,5 @@ void use_printf(float f, double d) } size_t three_chars(unsigned char a, unsigned char b, unsigned char c) { - return a * b * c; // at most 16581375 [FALSE POSITIVE] + return a * b * c; // at most 16581375 } diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected index 25cef782aeec..4a5edf213790 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.expected @@ -7,4 +7,3 @@ | IntMultToLong.c:61:23:61:33 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. | | IntMultToLong.c:63:23:63:40 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. | | IntMultToLong.c:75:9:75:13 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. | -| IntMultToLong.c:93:12:93:20 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'size_t'. | From a56376a2df90b4c28335303779d9187f84818251 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 24 Sep 2018 14:31:19 +0200 Subject: [PATCH 099/110] C++: rename effectivelyConstant to likelySmall This reflects the existing QLDoc better and makes it more clear why it includes char-typed expressions. --- cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index 4ab00c2d2804..885039e9d58d 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -27,13 +27,13 @@ import semmle.code.cpp.controlflow.SSA * In these cases the value of `e` is likely to be small and * controlled, so we consider it less likely to cause an overflow. */ -predicate effectivelyConstant(Expr e) { +predicate likelySmall(Expr e) { e.isConstant() or e.getType().getSize() <= 1 or e.(ArrayExpr).getArrayBase().getType().(ArrayType).getBaseType().isConst() or exists(SsaDefinition def, Variable v | def.getAUse(v) = e and - effectivelyConstant(def.getDefiningValue(v)) + likelySmall(def.getDefiningValue(v)) ) } @@ -58,7 +58,7 @@ int getEffectiveMulOperands(MulExpr me) { result = count(Expr op | op = getMulOperand*(me) and not op instanceof MulExpr and - not effectivelyConstant(op) + not likelySmall(op) ) } From c3b523cdc498f48a8e36fa285be86ea22c067398 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Mon, 24 Sep 2018 10:02:52 -0700 Subject: [PATCH 100/110] Fixing a missed reference to renamed file. --- cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp index d441e368b20c..d5b949691f01 100644 --- a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp @@ -15,7 +15,7 @@

    In the following example, HRESULT is used in a test expression incorrectly as it may yield an incorrect result.

    - +

    To fix this issue, use the FAILED macro in the test expression.

    From c75019726cfd31c594f1179a32497af3d45f517e Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Mon, 24 Sep 2018 10:10:58 -0700 Subject: [PATCH 101/110] Removing tabs & reformatting --- .../CWE-732/UnsafeDaclSecurityDescriptor.ql | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql index a7d17f4de329..d2165187b423 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -32,15 +32,17 @@ class SetSecurityDescriptorDaclFunctionConfiguration extends DataFlow::Configura SetSecurityDescriptorDaclFunctionConfiguration() { this = "SetSecurityDescriptorDaclFunctionConfiguration" } - + override predicate isSource(DataFlow::Node source) { - exists( NullValue nullExpr | + exists( + NullValue nullExpr | source.asExpr() = nullExpr ) } override predicate isSink(DataFlow::Node sink) { - exists( SetSecurityDescriptorDaclFunctionCall call, VariableAccess val | + exists( + SetSecurityDescriptorDaclFunctionCall call, VariableAccess val | val = sink.asExpr() | val = call.getArgument(2) ) @@ -48,14 +50,18 @@ class SetSecurityDescriptorDaclFunctionConfiguration extends DataFlow::Configura } from SetSecurityDescriptorDaclFunctionCall call, string message -where exists( NullValue nullExpr | - message = "Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object." | - call.getArgument(1).getValue().toInt() != 0 - and call.getArgument(2) = nullExpr - ) or exists( Expr constassign, VariableAccess var, - SetSecurityDescriptorDaclFunctionConfiguration config | - message = "Setting a SECURITY_DESCRIPTOR's DACL using variable " + var + " that is set to NULL will result in an unprotected object." | - var = call.getArgument(2) - and config.hasFlow(DataFlow::exprNode(constassign), DataFlow::exprNode(var)) - ) +where exists + ( + NullValue nullExpr | + message = "Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object." | + call.getArgument(1).getValue().toInt() != 0 + and call.getArgument(2) = nullExpr + ) or exists + ( + Expr constassign, VariableAccess var, + SetSecurityDescriptorDaclFunctionConfiguration config | + message = "Setting a SECURITY_DESCRIPTOR's DACL using variable " + var + " that is set to NULL will result in an unprotected object." | + var = call.getArgument(2) + and config.hasFlow(DataFlow::exprNode(constassign), DataFlow::exprNode(var)) + ) select call, message \ No newline at end of file From a566ffae4a18d0dbb253e1befaf0c90d19b26d0b Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Mon, 24 Sep 2018 10:18:39 -0700 Subject: [PATCH 102/110] Fixed the test .expected file --- .../CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected index b759bd907d83..41b2e91da4cf 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected @@ -1,2 +1,2 @@ -| UnsafeDaclSecurityDescriptor.cpp:69:6:69:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. | -| UnsafeDaclSecurityDescriptor.cpp:75:6:75:30 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR's DACL using variable pDacl that is set to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:70:9:70:33 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:76:9:76:33 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL using variable pDacl that is set to NULL will result in an unprotected object. | From 614a8ef09158e558cc4dd874fcbcd6835ecc28b8 Mon Sep 17 00:00:00 2001 From: Sauyon Lee Date: Thu, 13 Sep 2018 15:12:22 -0700 Subject: [PATCH 103/110] Allow void* pointer arithmetic with sizeof Technically this isn't allowed by the C spec, but it's been seen in the wild: see https://lgtm.com/projects/g/libcsp/libcsp/snapshot/3763c7b3380f95c81636de5c95156fd3ef151a21/files/src/csp_buffer.c\#x1d04047d2bb68c21:1 --- .../Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql index 75c02b5e8c7f..d2d16b552bed 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql +++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql @@ -13,18 +13,19 @@ import cpp import IncorrectPointerScalingCommon -private predicate isCharPtrExpr(Expr e) { +private predicate isCharSzPtrExpr(Expr e) { exists (PointerType pt | pt = e.getFullyConverted().getUnderlyingType() - | pt.getBaseType().getUnspecifiedType() instanceof CharType) + | pt.getBaseType().getUnspecifiedType() instanceof CharType + or pt.getBaseType().getUnspecifiedType() instanceof VoidType) } from Expr sizeofExpr, Expr e where // If we see an addWithSizeof then we expect the type of - // the pointer expression to be char*. Otherwise it is probably - // a mistake. - addWithSizeof(e, sizeofExpr, _) and not isCharPtrExpr(e) + // the pointer expression to be char* or void*. Otherwise it + // is probably a mistake. + addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e) select sizeofExpr, "Suspicious sizeof offset in a pointer arithmetic expression. " + From 1ab11109f9360dbdbd171a7868c0b3d1dc8d0250 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Mon, 24 Sep 2018 16:57:45 +0100 Subject: [PATCH 104/110] JavaScript: Add new query flagging identity replacements. --- change-notes/1.19/analysis-javascript.md | 1 + .../config/suites/javascript/correctness-core | 1 + .../ql/src/RegExp/IdentityReplacement.qhelp | 36 +++++++++++++++ .../ql/src/RegExp/IdentityReplacement.ql | 46 +++++++++++++++++++ .../RegExp/examples/IdentityReplacement.js | 1 + .../examples/IdentityReplacementGood.js | 1 + .../IdentityReplacement.expected | 4 ++ .../IdentityReplacement.js | 1 + .../IdentityReplacement.qlref | 1 + .../IdentityReplacementGood.js | 1 + .../RegExp/IdentityReplacement/tst.js | 4 ++ 11 files changed, 97 insertions(+) create mode 100644 javascript/ql/src/RegExp/IdentityReplacement.qhelp create mode 100644 javascript/ql/src/RegExp/IdentityReplacement.ql create mode 100644 javascript/ql/src/RegExp/examples/IdentityReplacement.js create mode 100644 javascript/ql/src/RegExp/examples/IdentityReplacementGood.js create mode 100644 javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected create mode 100644 javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.js create mode 100644 javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.qlref create mode 100644 javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacementGood.js create mode 100644 javascript/ql/test/query-tests/RegExp/IdentityReplacement/tst.js diff --git a/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 4415af95d30c..106ba3d0fd1f 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -14,6 +14,7 @@ |-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). Results are not shown on LGTM by default. | | Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. | +| Replacement of a substring with itself (`js/identity-replacement`) | correctness, security, external/cwe/cwe-116 | Highlights string replacements that replace a string with itself, which usually indicates a mistake. Results shown on LGTM by default. | ## Changes to existing queries diff --git a/javascript/config/suites/javascript/correctness-core b/javascript/config/suites/javascript/correctness-core index 1629edb6e9bc..344e8936a38d 100644 --- a/javascript/config/suites/javascript/correctness-core +++ b/javascript/config/suites/javascript/correctness-core @@ -32,6 +32,7 @@ + semmlecode-javascript-queries/RegExp/BackrefIntoNegativeLookahead.ql: /Correctness/Regular Expressions + semmlecode-javascript-queries/RegExp/DuplicateCharacterInCharacterClass.ql: /Correctness/Regular Expressions + semmlecode-javascript-queries/RegExp/EmptyCharacterClass.ql: /Correctness/Regular Expressions ++ semmlecode-javascript-queries/RegExp/IdentityReplacement.ql: /Correctness/Regular Expressions + semmlecode-javascript-queries/RegExp/UnboundBackref.ql: /Correctness/Regular Expressions + semmlecode-javascript-queries/RegExp/UnmatchableCaret.ql: /Correctness/Regular Expressions + semmlecode-javascript-queries/RegExp/UnmatchableDollar.ql: /Correctness/Regular Expressions diff --git a/javascript/ql/src/RegExp/IdentityReplacement.qhelp b/javascript/ql/src/RegExp/IdentityReplacement.qhelp new file mode 100644 index 000000000000..f1610a782624 --- /dev/null +++ b/javascript/ql/src/RegExp/IdentityReplacement.qhelp @@ -0,0 +1,36 @@ + + + + +

    +Replacing a substring with itself has no effect and usually indicates a mistake, such as +misspelling a backslash escape. +

    +
    + + +

    +Examine the string replacement to find and correct any typos. +

    +
    + + +

    +The following code snippet attempts to backslash-escape all double quotes in raw +by replacing all instances of " with \": +

    + +

    +However, the replacement string '\"' is actually the same as '"', +with \" interpreted as an identity escape, so the replacement does nothing. +Instead, the replacement string should be '\\"': +

    + +
    + + +
  • Mozilla Developer Network: String escape notation.
  • +
    +
    diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql new file mode 100644 index 000000000000..7ffbd83c3b40 --- /dev/null +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -0,0 +1,46 @@ +/** + * @name Replacement of a substring with itself + * @description Replacing a substring with itself has no effect and may indicate a mistake. + * @kind problem + * @problem.severity warning + * @id js/identity-replacement + * @precision very-high + * @tags correctness + * security + * external/cwe/cwe-116 + */ + +import javascript + +/** + * Holds if `e`, when used as the first argument of `String.prototype.replace`, matches + * `s` and nothing else. + */ +predicate matchesString(Expr e, string s) { + exists (RegExpConstant c | + matchesConstant(e.(RegExpLiteral).getRoot(), c) and + s = c.getValue() + ) + or + s = e.getStringValue() +} + +/** + * Holds if `t` matches `c` and nothing else. + */ +predicate matchesConstant(RegExpTerm t, RegExpConstant c) { + c = t + or + matchesConstant(t.(RegExpGroup).getAChild(), c) + or + exists (RegExpCharacterClass recc | recc = t and not recc.isInverted() | + recc.getNumChild() = 1 and + matchesConstant(recc.getChild(0), c) + ) +} + +from MethodCallExpr repl, string s +where repl.getMethodName() = "replace" and + matchesString(repl.getArgument(0), s) and + repl.getArgument(1).getStringValue() = s +select repl.getArgument(0), "This replaces '" + s + "' with itself." diff --git a/javascript/ql/src/RegExp/examples/IdentityReplacement.js b/javascript/ql/src/RegExp/examples/IdentityReplacement.js new file mode 100644 index 000000000000..1c5d9251e7a4 --- /dev/null +++ b/javascript/ql/src/RegExp/examples/IdentityReplacement.js @@ -0,0 +1 @@ +var escaped = raw.replace(/"/g, '\"'); diff --git a/javascript/ql/src/RegExp/examples/IdentityReplacementGood.js b/javascript/ql/src/RegExp/examples/IdentityReplacementGood.js new file mode 100644 index 000000000000..0238fc1fe3f4 --- /dev/null +++ b/javascript/ql/src/RegExp/examples/IdentityReplacementGood.js @@ -0,0 +1 @@ +var escaped = raw.replace(/"/g, '\\"'); diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected new file mode 100644 index 000000000000..58972c3af834 --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected @@ -0,0 +1,4 @@ +| IdentityReplacement.js:1:27:1:30 | /"/g | This replaces '"' with itself. | +| tst.js:1:13:1:16 | "\\\\" | This replaces '\\' with itself. | +| tst.js:2:13:2:18 | /(\\\\)/ | This replaces '\\' with itself. | +| tst.js:3:13:3:17 | /["]/ | This replaces '"' with itself. | diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.js b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.js new file mode 100644 index 000000000000..1c5d9251e7a4 --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.js @@ -0,0 +1 @@ +var escaped = raw.replace(/"/g, '\"'); diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.qlref b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.qlref new file mode 100644 index 000000000000..f8b9c39b11ff --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.qlref @@ -0,0 +1 @@ +RegExp/IdentityReplacement.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacementGood.js b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacementGood.js new file mode 100644 index 000000000000..0238fc1fe3f4 --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacementGood.js @@ -0,0 +1 @@ +var escaped = raw.replace(/"/g, '\\"'); diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/tst.js b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/tst.js new file mode 100644 index 000000000000..bdcc89a010d9 --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/tst.js @@ -0,0 +1,4 @@ +raw.replace("\\", "\\"); // NOT OK +raw.replace(/(\\)/, "\\"); // NOT OK +raw.replace(/["]/, "\""); // NOT OK +raw.replace("\\", "\\\\"); // OK From ec9a3c87a7cdcea17b472bedf4e954f47ee4637e Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 25 Sep 2018 11:02:03 +0100 Subject: [PATCH 105/110] JavaScript: Do not flag case-insensitive replace. --- javascript/ql/src/RegExp/IdentityReplacement.ql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql index 7ffbd83c3b40..0163016bad32 100644 --- a/javascript/ql/src/RegExp/IdentityReplacement.ql +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -17,9 +17,10 @@ import javascript * `s` and nothing else. */ predicate matchesString(Expr e, string s) { - exists (RegExpConstant c | - matchesConstant(e.(RegExpLiteral).getRoot(), c) and - s = c.getValue() + exists (RegExpLiteral rl | + rl = e and + not rl.isIgnoreCase() and + regExpMatchesString(rl.getRoot(), s) ) or s = e.getStringValue() From 5fb22ba02157ab6483181733b431f4e0105dfa33 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 25 Sep 2018 11:02:45 +0100 Subject: [PATCH 106/110] JavaScript: Handle zero-width assertions and sequences. --- .../ql/src/RegExp/IdentityReplacement.ql | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql index 0163016bad32..c735b4377b96 100644 --- a/javascript/ql/src/RegExp/IdentityReplacement.ql +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -27,16 +27,36 @@ predicate matchesString(Expr e, string s) { } /** - * Holds if `t` matches `c` and nothing else. + * Holds if `t` matches `s` and nothing else. */ -predicate matchesConstant(RegExpTerm t, RegExpConstant c) { - c = t +language[monotonicAggregates] +predicate regExpMatchesString(RegExpTerm t, string s) { + // constants match themselves + s = t.(RegExpConstant).getValue() or - matchesConstant(t.(RegExpGroup).getAChild(), c) + // assertions match the empty string + (t instanceof RegExpCaret or + t instanceof RegExpDollar or + t instanceof RegExpWordBoundary or + t instanceof RegExpNonWordBoundary or + t instanceof RegExpLookahead or + t instanceof RegExpLookbehind) and + s = "" or + // groups match their content + regExpMatchesString(t.(RegExpGroup).getAChild(), s) + or + // single-character classes match that character exists (RegExpCharacterClass recc | recc = t and not recc.isInverted() | recc.getNumChild() = 1 and - matchesConstant(recc.getChild(0), c) + regExpMatchesString(recc.getChild(0), s) + ) + or + // sequences match the concatenation of their elements + exists (RegExpSequence seq | seq = t | + s = concat(int i, RegExpTerm child | child = seq.getChild(i) | + any(string subs | regExpMatchesString(child, subs)) order by i + ) ) } From 659c67c715a6ab3d1454670fb5b920d4980ef5c1 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 25 Sep 2018 11:03:05 +0100 Subject: [PATCH 107/110] JavaScript: Produce friendlier message for empty-string replacements. --- javascript/ql/src/RegExp/IdentityReplacement.ql | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/RegExp/IdentityReplacement.ql b/javascript/ql/src/RegExp/IdentityReplacement.ql index c735b4377b96..ecb02733337a 100644 --- a/javascript/ql/src/RegExp/IdentityReplacement.ql +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -60,8 +60,9 @@ predicate regExpMatchesString(RegExpTerm t, string s) { ) } -from MethodCallExpr repl, string s +from MethodCallExpr repl, string s, string friendly where repl.getMethodName() = "replace" and matchesString(repl.getArgument(0), s) and - repl.getArgument(1).getStringValue() = s -select repl.getArgument(0), "This replaces '" + s + "' with itself." + repl.getArgument(1).getStringValue() = s and + (if s = "" then friendly = "the empty string" else friendly = "'" + s + "'") +select repl.getArgument(0), "This replaces " + friendly + " with itself." From 0e63ea1b511a0a4017fc0dfd77df79978ccac935 Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Tue, 25 Sep 2018 11:03:15 +0100 Subject: [PATCH 108/110] JavaScript: Update tests. --- .../IdentityReplacement/IdentityReplacement.expected | 8 ++++++++ .../query-tests/RegExp/IdentityReplacement/tst.js | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected index 58972c3af834..e707ae9ecdd2 100644 --- a/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected @@ -2,3 +2,11 @@ | tst.js:1:13:1:16 | "\\\\" | This replaces '\\' with itself. | | tst.js:2:13:2:18 | /(\\\\)/ | This replaces '\\' with itself. | | tst.js:3:13:3:17 | /["]/ | This replaces '"' with itself. | +| tst.js:6:13:6:18 | /foo/g | This replaces 'foo' with itself. | +| tst.js:9:13:9:17 | /^\\\\/ | This replaces '\\' with itself. | +| tst.js:10:13:10:17 | /\\\\$/ | This replaces '\\' with itself. | +| tst.js:11:13:11:18 | /\\b\\\\/ | This replaces '\\' with itself. | +| tst.js:12:13:12:18 | /\\B\\\\/ | This replaces '\\' with itself. | +| tst.js:13:13:13:22 | /\\\\(?!\\\\)/ | This replaces '\\' with itself. | +| tst.js:14:13:14:23 | /(? Date: Tue, 25 Sep 2018 10:50:34 -0700 Subject: [PATCH 109/110] Removing all usage of single quotes --- .../CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp | 2 +- .../Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql | 8 ++++---- .../CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp index 79e2285201aa..519d21fd8c17 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.qhelp @@ -4,7 +4,7 @@ -

    This query indicates that a call is setting the SECURITY_DESCRIPTOR's DACL field to null.

    +

    This query indicates that a call is setting the DACL field in a SECURITY_DESCRIPTOR to null.

    When using SetSecurityDescriptorDacl to set a discretionary access control (DACL), setting the bDaclPresent argument to TRUE indicates the prescence of a DACL in the security description in the argument pDacl.

    When the pDacl parameter does not point to a DACL (i.e. it is NULL) and the bDaclPresent flag is TRUE, a NULL DACL is specified.

    A NULL DACL grants full access to any user who requests it; normal security checking is not performed with respect to the object.

    diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql index d2165187b423..068c4e442891 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -1,6 +1,6 @@ /** - * @name Setting a SECURITY_DESCRIPTOR's DACL to NULL - * @description Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. + * @name Setting a DACL to NULL in a SECURITY_DESCRIPTOR + * @description Setting a DACL to NULL in a SECURITY_DESCRIPTOR will result in an unprotected object. * If the DACL that belongs to the security descriptor of an object is set to NULL, a null DACL is created. * A null DACL grants full access to any user who requests it; * normal security checking is not performed with respect to the object. @@ -53,14 +53,14 @@ from SetSecurityDescriptorDaclFunctionCall call, string message where exists ( NullValue nullExpr | - message = "Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object." | + message = "Setting a DACL to NULL in a SECURITY_DESCRIPTOR will result in an unprotected object." | call.getArgument(1).getValue().toInt() != 0 and call.getArgument(2) = nullExpr ) or exists ( Expr constassign, VariableAccess var, SetSecurityDescriptorDaclFunctionConfiguration config | - message = "Setting a SECURITY_DESCRIPTOR's DACL using variable " + var + " that is set to NULL will result in an unprotected object." | + message = "Setting a DACL to NULL in a SECURITY_DESCRIPTOR using variable " + var + " that is set to NULL will result in an unprotected object." | var = call.getArgument(2) and config.hasFlow(DataFlow::exprNode(constassign), DataFlow::exprNode(var)) ) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected index 41b2e91da4cf..e596bae3a0f0 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected @@ -1,2 +1,2 @@ -| UnsafeDaclSecurityDescriptor.cpp:70:9:70:33 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL to NULL will result in an unprotected object. | -| UnsafeDaclSecurityDescriptor.cpp:76:9:76:33 | call to SetSecurityDescriptorDacl | Setting a SECURITY_DESCRIPTOR\u2019s DACL using variable pDacl that is set to NULL will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:70:9:70:33 | call to SetSecurityDescriptorDacl | Setting a DACL to NULL in a SECURITY_DESCRIPTOR will result in an unprotected object. | +| UnsafeDaclSecurityDescriptor.cpp:76:9:76:33 | call to SetSecurityDescriptorDacl | Setting a DACL to NULL in a SECURITY_DESCRIPTOR using variable pDacl that is set to NULL will result in an unprotected object. | From 4ff79b046d43dfd7a0b583ee5fee214f50b5d9c2 Mon Sep 17 00:00:00 2001 From: Aditya Sharad Date: Wed, 26 Sep 2018 17:56:21 +0100 Subject: [PATCH 110/110] C++: Update HashCons test output. --- .../valuenumbering/HashCons/HashCons.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected index c050ac2bffd0..656a4c888f62 100644 --- a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -67,10 +67,10 @@ | test.cpp:156:3:156:9 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 | | test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 | | test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 | -| test.cpp:179:10:179:22 | (...) | 179:c10-c22 179:c10-c22 | -| test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 | -| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 | -| test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 | +| test.cpp:179:10:179:22 | (...) | 179:c10-c22 | +| test.cpp:179:17:179:17 | y | 179:c17-c17 | +| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 | +| test.cpp:185:17:185:17 | y | 185:c17-c17 | | test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 | | test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 245:c25-c35 246:c25-c35 | | test.cpp:206:3:206:21 | alignof(int_holder) | 206:c25-c43 206:c3-c21 |