diff --git a/.editorconfig b/.editorconfig index 476ae898a0f8..270106b1facf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,2 @@ -[*.{ql,qll,qlref,dbscheme,qhelp}] -end_of_line = lf +[*] +end_of_line = lf diff --git a/.gitattributes b/.gitattributes index a6c5703f96bc..626c5a297c83 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,17 +1,48 @@ -# 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 + +# 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 + +# 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 diff --git a/.gitignore b/.gitignore index 31f8ccd9abf2..7e82b2f488ca 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,8 @@ # 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/CODEOWNERS b/CODEOWNERS index 503c68f963ed..d6d1d4898a64 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,2 +1,3 @@ /csharp/ @Semmle/cs +/java/ @Semmle/java /javascript/ @Semmle/js diff --git a/change-notes/1.19/analysis-cpp.md b/change-notes/1.19/analysis-cpp.md new file mode 100644 index 000000000000..5a3340603c87 --- /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** | +|----------------------------|------------------------|------------------------------------------------------------------| +| 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/change-notes/1.19/analysis-javascript.md b/change-notes/1.19/analysis-javascript.md index 5fdf442d1289..f68940ae6f77 100644 --- a/change-notes/1.19/analysis-javascript.md +++ b/change-notes/1.19/analysis-javascript.md @@ -2,17 +2,27 @@ ## General improvements +* 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** | +|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 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 | **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. | +| 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 additional CSRF protection middlewares. | ## Changes to QL libraries diff --git a/config/identical-files.json b/config/identical-files.json index bb1c5dca83ca..829837ceed40 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/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/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/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/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index ac2bed3d6f99..885039e9d58d 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -21,17 +21,19 @@ 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 * 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)) ) } @@ -56,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) ) } 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-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 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/Security/CWE/CWE-253/HResultBooleanConversion.cpp b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.cpp new file mode 100644 index 000000000000..44896e89e153 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.cpp @@ -0,0 +1,7 @@ +LPMALLOC pMalloc; +HRESULT hr = CoGetMalloc(1, &pMalloc); + +if (!hr) +{ + // code ... +} diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.qhelp new file mode 100644 index 000000000000..d5b949691f01 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.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-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-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. " + 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..1324185481f4 --- /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..519d21fd8c17 --- /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 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.

+
+ + +

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..068c4e442891 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -0,0 +1,67 @@ +/** + * @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. + * @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 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 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)) + ) +select call, message \ No newline at end of file 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() 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..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,28 +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 or result.getRValue() instanceof NewArrayExpr) 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/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/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/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/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/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..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 + } } /** @@ -301,6 +309,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 +569,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/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll new file mode 100644 index 000000000000..cff349a07b44 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll @@ -0,0 +1,274 @@ +private import internal.ValueNumberingInternal +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 + 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) + } + +/** + * 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() + ) + } +} + +/** + * 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 + 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. + */ +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, 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, + 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/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll new file mode 100644 index 000000000000..d55844c04712 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.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/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index aad29a8c442b..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) } @@ -195,6 +197,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/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..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 + } } /** @@ -301,6 +309,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 +569,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/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll new file mode 100644 index 000000000000..cff349a07b44 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll @@ -0,0 +1,274 @@ +private import internal.ValueNumberingInternal +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 + 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) + } + +/** + * 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() + ) + } +} + +/** + * 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 + 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. + */ +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, 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, + 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/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll new file mode 100644 index 000000000000..3b28a05290c4 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.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/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/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..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 + } } /** @@ -301,6 +309,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 +569,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/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll new file mode 100644 index 000000000000..cff349a07b44 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll @@ -0,0 +1,274 @@ +private import internal.ValueNumberingInternal +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 + 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) + } + +/** + * 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() + ) + } +} + +/** + * 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 + 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. + */ +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, 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, + 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/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll new file mode 100644 index 000000000000..9b4f813a10bd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR 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..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) } @@ -195,6 +197,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/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll new file mode 100644 index 000000000000..07923d60b034 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll @@ -0,0 +1,1095 @@ +/** + * Provides an implementation of Hash consing. + * See https://en.wikipedia.org/wiki/Hash_consing + * + * 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 `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 + * 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_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,_)} + or + HC_Nullptr() {mk_Nullptr(_)} + or + HC_Variable(Variable x) { + mk_Variable(x, _) + } + or + HC_FieldAccess(HashCons s, Field f) { + mk_DotFieldAccess(s,f,_) + } + or + HC_Deref(HashCons p) { + mk_Deref(p,_) + } + or + HC_PointerFieldAccess(HashCons qual, Field target) { + mk_PointerFieldAccess(qual, target, _) + } + or + HC_ThisExpr(Function 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, _) } + or + HC_BinaryOp(HashCons lhs, HashCons rhs, string opname) { + mk_BinaryOp(lhs, rhs, opname, _) + } + or + HC_UnaryOp(HashCons child, string opname) { mk_UnaryOp(child, opname, _) } + or + HC_ArrayAccess(HashCons x, HashCons i) { + mk_ArrayAccess(x,i,_) + } + or + 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, _) + } + or + // 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, _)} + 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 + 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, _) + } + or + HC_ArrayAggregateLiteral(Type t, HC_Array hca) { + mk_ArrayAggregateLiteral(t, hca, _) + } + or + HC_DeleteExpr(HashCons child) {mk_DeleteExpr(child, _)} + or + HC_DeleteArrayExpr(HashCons child) {mk_DeleteArrayExpr(child, _)} + or + HC_ThrowExpr(HashCons child) {mk_ThrowExpr(child, _)} + 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,_) } + +/** Used to implement optional init on `new` expressions */ +private newtype HC_Init = + HC_NoInit() + or + HC_HasInit(HashCons hc) {mk_HasInit(hc, _)} + +/** + * Used to implement optional allocator call on `new` expressions + */ +private newtype HC_Alloc = + HC_NoAlloc() + or + 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() { + any() + } + or HC_ArgCons(HashCons hc, int i, HC_Args list) { + mk_ArgCons(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().getUnspecifiedType() = 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 + * 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 HashCons extends 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_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 + 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 + 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 + 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 = "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 + 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 + if this instanceof HC_AllocatorArgZero then result = "AllocatorArgZero" 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 exampleLocationString(e.getLocation()), e.getLocation().getStartColumn(), + e.getLocation().getEndLine(), e.getLocation().getEndColumn()) + } + + /** Gets a textual representation of this element. */ + string toString() { + result = exampleExpr().toString() + } + + /** Gets the primary location of this element. */ + Location getLocation() { + result = exampleExpr().getLocation() + } +} + +/** + * 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 + 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() +} + +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 + 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() +} + +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 and + e.getType().getUnspecifiedType().(ArrayType).getBaseType() instanceof CharType +} + +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 +} + +private predicate mk_DotFieldAccess( + HashCons 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 +} + +private predicate mk_PointerFieldAccess( + HashCons qualifier, Field target, + PointerFieldAccess access) { + analyzablePointerFieldAccess(access) and + target = access.getTarget() and + qualifier = hashCons(access.getQualifier().getFullyConverted()) +} + +private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) { + strictcount (access.getTarget()) = 1 and + strictcount (access.getEnclosingFunction()) = 1 +} + +private predicate mk_ImplicitThisFieldAccess(Function fcn, Field target, + ImplicitThisFieldAccess access) { + analyzableImplicitThisFieldAccess(access) and + target = access.getTarget() and + fcn = access.getEnclosingFunction() +} + +private predicate analyzableVariable(VariableAccess access) { + not (access instanceof FieldAccess) and + strictcount (access.getTarget()) = 1 +} + +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 +} + +private predicate mk_Conversion(Type t, HashCons child, Conversion conv) { + analyzableConversion(conv) and + t = conv.getType().getUnspecifiedType() and + child = hashCons(conv.getExpr()) +} + +private predicate analyzableBinaryOp(BinaryOperation op) { + strictcount (op.getLeftOperand().getFullyConverted()) = 1 and + strictcount (op.getRightOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 +} + +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 + opname = op.getOperator() +} + +private predicate analyzableUnaryOp(UnaryOperation op) { + not (op instanceof PointerDereferenceExpr) and + strictcount (op.getOperand().getFullyConverted()) = 1 and + strictcount (op.getOperator()) = 1 +} + +private predicate mk_UnaryOp(HashCons 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 +} + +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 +} + +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) { + strictcount (deref.getOperand().getFullyConverted()) = 1 +} + +private predicate mk_Deref(HashCons p, PointerDereferenceExpr deref) { + analyzablePointerDereferenceExpr(deref) and + p = hashCons(deref.getOperand().getFullyConverted()) +} + +private predicate analyzableNonmemberFunctionCall(FunctionCall fc) { + forall(int i | + i in [0..fc.getNumberOfArguments()-1] | + 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 +) { + fc.getTarget() = fcn and + analyzableNonmemberFunctionCall(fc) and + ( + exists(HashCons head, HC_Args tail | + mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc) + ) + or + fc.getNumberOfArguments() = 0 and + args = HC_EmptyArgs() + ) +} + +private predicate analyzableExprCall(ExprCall ec) { + forall(int i | + i in [0..ec.getNumberOfArguments()-1] | + 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 | + mk_ArgConsInner(head, tail, ec.getNumberOfArguments() - 1, args, ec) + ) + or + ec.getNumberOfArguments() = 0 and + args = HC_EmptyArgs() + ) +} + +private predicate analyzableMemberFunctionCall( + FunctionCall fc) { + forall(int i | + i in [0..fc.getNumberOfArguments()-1] | + strictcount(fc.getArgument(i).getFullyConverted()) = 1 + ) and + strictcount(fc.getTarget()) = 1 and + strictcount(fc.getQualifier().getFullyConverted()) = 1 +} + +private predicate mk_MemberFunctionCall( + Function fcn, + HashCons qual, + HC_Args args, + FunctionCall fc +) { + fc.getTarget() = fcn and + analyzableMemberFunctionCall(fc) and + hashCons(fc.getQualifier().getFullyConverted()) = qual and + ( + exists(HashCons head, HC_Args tail | + mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc) + ) + or + fc.getNumberOfArguments() = 0 and + args = HC_EmptyArgs() + ) +} + +private predicate analyzableCall(Call c) { + analyzableNonmemberFunctionCall(c) + or + analyzableMemberFunctionCall(c) + or + 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(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 | + mk_ArgConsInner(head, tail, i-1, list, c) and + i > 0 + ) + or + i = 0 and + list = HC_EmptyArgs() + ) +} + +// 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) +} + +/* + * The 0th argument of an allocator call in a new expression is always an error expression; + * this works around it + */ +private predicate analyzableAllocatorArgZero(ErrorExpr e) { + exists(NewOrNewArrayExpr new | + 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() + ) +} + +private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) { + hc = hashCons(new.(NewExpr).getInitializer().getFullyConverted()) or + hc = hashCons(new.(NewArrayExpr).getInitializer().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().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, NewExpr new) { + analyzableNewExpr(new) and + t = new.getAllocatedType().getUnspecifiedType() and + ( + alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted())) + or + not exists(new.getAllocatorCall().getFullyConverted()) and + alloc = HC_NoAlloc() + ) + and + ( + init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted())) + or + not exists(new.getInitializer().getFullyConverted()) and + init = HC_NoInit() + ) +} + +private predicate analyzableNewArrayExpr(NewArrayExpr new) { + strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and + count(new.getAllocatorCall().getFullyConverted()) <= 1 and + count(new.getInitializer().getFullyConverted()) <= 1 and + count(new.(NewArrayExpr).getExtent().getFullyConverted()) <= 1 +} + +private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Extent extent, HC_Init init, NewArrayExpr new) { + analyzableNewArrayExpr(new) and + t = new.getAllocatedType() and + + ( + alloc = HC_HasAlloc(hashCons(new.getAllocatorCall().getFullyConverted())) + or + not exists(new.getAllocatorCall().getFullyConverted()) and + alloc = HC_NoAlloc() + ) + and + ( + init = HC_HasInit(hashCons(new.getInitializer().getFullyConverted())) + or + not exists(new.getInitializer().getFullyConverted()) and + init = HC_NoInit() + ) + and + + ( + extent = HC_HasExtent(hashCons(new.getExtent().getFullyConverted())) + or + not exists(new.getExtent().getFullyConverted()) and + extent = HC_NoExtent() + ) +} + +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 +} + +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 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) { + count(e.getAChild()) = 0 and + strictcount(e.getResultType()) = 1 +} + +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 +} + +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()) +} + +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 + 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 + f2.getInitializationOrder() = i-1 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 and + strictcount(Field f, int j | + cal.getFieldExpr(f) = cal.getChild(i) and j = f.getInitializationOrder()) = 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 | + 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) + ) + 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 + ) 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 + ( + 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) + ) +} + +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() +} + +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().getFullyConverted() = child.getAnExpr() +} + + +/** Gets the hash-cons of expression `e`. */ +cached HashCons hashCons(Expr e) { + exists (int val, Type t + | 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)) + or + exists (string val, Type t + | mk_StringLiteral(val, t, e) and + result = HC_StringLiteral(val, t)) + or + exists (Variable x + | mk_Variable(x, e) and + result = HC_Variable(x)) + or + exists (HashCons qualifier, Field target + | mk_DotFieldAccess(qualifier, target, e) and + result = HC_FieldAccess(qualifier, target)) + or + exists (HashCons qualifier, Field target + | mk_PointerFieldAccess(qualifier, target, e) and + result = HC_PointerFieldAccess(qualifier, target)) + or + 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 + result = HC_ThisExpr(fcn)) + or + exists (Type t, HashCons child + | mk_Conversion(t, child, e) and + result = HC_Conversion(t, child)) + or + exists (HashCons lhs, HashCons rhs, string opname + | mk_BinaryOp(lhs, rhs, opname, e) and + result = HC_BinaryOp(lhs, rhs, opname)) + or + exists (HashCons child, string opname + | mk_UnaryOp(child, opname, e) and + result = HC_UnaryOp(child, opname)) + or + exists (HashCons x, HashCons i + | mk_ArrayAccess(x, i, e) and + result = HC_ArrayAccess(x, i)) + or + exists (HashCons 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(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) + ) + or + // works around an extractor issue + 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_Extent extent, HC_Init init + | mk_NewArrayExpr(t, alloc, extent, init, e) and + result = HC_NewArrayExpr(t, alloc, extent, 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_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_UuidofOperator(t, e) and + result = HC_UuidofOperator(t) + ) + 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 + 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 + 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 + exists(HashCons child + | mk_ThrowExpr(child, e) and + result = HC_ThrowExpr(child) + ) + or + ( + mk_ReThrowExpr(e) and + 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() + ) + + or + (not analyzableExpr(e,_) and result = HC_Unanalyzable(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) { + (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 + (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") 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") or + (analyzableConditionalExpr(e) and kind = "ConditionalExpr") or + (analyzableNoExceptExpr(e) and kind = "NoExceptExpr") or + (analyzableAllocatorArgZero(e) and kind = "AllocatorArgZero") +} 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 new file mode 100644 index 000000000000..3df04157bf62 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -0,0 +1,714 @@ +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 : + +# 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 : + +# 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/ir_gvn.ql b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.ql new file mode 100644 index 000000000000..97d59c733317 --- /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.ValueNumbering diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp index 9c4a8bdd2548..d18ffed10eaa 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 diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected new file mode 100644 index 000000000000..656a4c888f62 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected @@ -0,0 +1,131 @@ +| 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: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:55:5:55:7 | ptr | 55:c5-c7 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-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:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 | +| 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:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 | +| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-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: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: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: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 | +| 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 | +| 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: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: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 | 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: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: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:263:3:263:32 | delete[] | 263:c3-c32 264:c3-c32 | +| 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: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 | +| 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: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: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 | diff --git a/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql new file mode 100644 index 000000000000..577e6ac62de1 --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.ql @@ -0,0 +1,12 @@ +import cpp +import semmle.code.cpp.valuenumbering.HashCons + +from HashCons 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..d953b53cd49e --- /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 HC. +// So this query should have zero results. +from Expr e +where count(hashCons(e)) != 1 +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 new file mode 100644 index 000000000000..56190e8af2ac --- /dev/null +++ b/cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp @@ -0,0 +1,334 @@ +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 +} + +// 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.5; + y = 0.5; +} + +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(); // getInt() and this->getInt() should be the same + } +}; + +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++ +} + +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; +} + +#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); +} + +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, 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, 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(int y, int z) { + new int(1); + new int(1); + new int(2); + + int x; + + char *ptr1 = new char[sizeof(int)]; + char *ptr2 = new char[sizeof(int)]; + + delete new(ptr1) IntHolder; + delete new(ptr1) IntHolder; + delete new(ptr2) IntHolder; + + delete new(32, ptr1) IntHolder; + delete[] new(32, ptr1) IntHolder; + new(32, ptr2) IntHolder; + new(16, ptr1) IntHolder; + + new(32) IntHolder; + new(32) IntHolder; + + new(32) IntHolder[10]; + new(32) IntHolder[10]; + + 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}; + + new(32) int[2] {}; + new(32) int[2] {}; + new(32) int[3] {}; + + new int[x]; + new int[x]; + new int[z]; +} + +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 + }; +} + +void test18() { + throw 1; + throw 1; + throw 2; + throw 2; + throw; + throw; +} + +void test19(int *x, int *y) { + x[0]; + x[0]; + x[1]; + x[1]; + 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; +} 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..ba9355dd6018 --- /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/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/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/Arithmetic/IntMultToLong/IntMultToLong.c b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/IntMultToLong/IntMultToLong.c index 603fd50b7747..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 @@ -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 +} 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/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-732/UnsafeDaclSecurityDescriptor.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp new file mode 100644 index 000000000000..8c5e0e632d84 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.cpp @@ -0,0 +1,92 @@ +// semmle-extractor-options: --microsoft +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..e596bae3a0f0 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.expected @@ -0,0 +1,2 @@ +| 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. | 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 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/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..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 @@ -11,5 +11,9 @@ | 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. | | 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. | +| 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/PlacementNew.cpp b/cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/PlacementNew.cpp new file mode 100644 index 000000000000..c7794857cb94 --- /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 [NOT DETECTED] + p3 = new (buffer_ptr) MyClassForPlacementNew(3); // GOOD: placement new, not an allocation + } + + ~MyTestForPlacementNew() + { + } + + MyClassForPlacementNew *p1, *p2, *p3; + char buffer[sizeof(MyClassForPlacementNew)]; +}; 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..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 @@ -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 + } + + ~MyClass6() + { + } + + int *a, *b, *c; +}; 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:; +} 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/.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/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/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/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/.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/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/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/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/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 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..7ed66a396cfd --- /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..7ed66a396cfd --- /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/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/config/suites/javascript/security b/javascript/config/suites/javascript/security index 26debb63a24e..2f0fd0dabc0f 100644 --- a/javascript/config/suites/javascript/security +++ b/javascript/config/suites/javascript/security @@ -1,7 +1,9 @@ + 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 ++ 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/Electron/EnablingNodeIntegration.qhelp b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp new file mode 100644 index 000000000000..34efea3b347e --- /dev/null +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.qhelp @@ -0,0 +1,58 @@ + + + + +

    + + 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. Always set nodeIntegration preference + to false before loading remote web sites, and only enable + it for whitelisted sites. + +

    + +

    + + Note that the nodeIntegration property is enabled + by default in Electron and needs to be set to false + explicitly. + +

    +
    + + + +

    + + The following examples shows insecure and secure uses of + BrowserWindow and BrowserView when loading + remote 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..d8e764012a20 --- /dev/null +++ b/javascript/ql/src/Electron/EnablingNodeIntegration.ql @@ -0,0 +1,34 @@ +/** + * @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 + +/** + * 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 + exists (string feature | + feature = "nodeIntegration" | + not exists(pref.getAPropertyWrite(feature)) and + result = "The `" + feature + "` feature is enabled by default." + ) +} + +from Electron::WebPreferences preferences +select preferences, getNodeIntegrationWarning(preferences) \ 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..87510f062650 --- /dev/null +++ b/javascript/ql/src/Electron/examples/EnablingNodeIntegration.js @@ -0,0 +1,21 @@ +//BAD: `nodeIntegration` enabled by default +var win_1 = new BrowserWindow(); +win_1.loadURL(remote_site); + +//BAD: `nodeIntegration` enabled +var win_2 = new BrowserWindow({webPreferences: {nodeIntegration: true}}); +win_2.loadURL(remote_site); + +//GOOD: `nodeIntegration` disabled +let win_3 = new BrowserWindow({webPreferences: {nodeIntegration: false}}); +win_3.loadURL(remote_site); + +//BAD: `nodeIntegration` enabled in the view +var win_4 = new BrowserWindow({webPreferences: {nodeIntegration: false}}) +var view_4 = new BrowserView({ + webPreferences: { + nodeIntegration: true + } +}); +win_4.setBrowserView(view_4); +view_4.webContents.loadURL(remote_site); diff --git a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql index 63212b69bf18..de947b250ab6 100644 --- a/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql +++ b/javascript/ql/src/Expressions/UnboundEventHandlerReceiver.ql @@ -10,20 +10,48 @@ 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 | bindingMethod.getDeclaringClass() = method.getDeclaringClass() and not bindingMethod.isStatic() and - thiz.getBinder().getAstNode() = bindingMethod.getBody() and - exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind | - // this. = .bind(...) - thiz.hasPropertyWrite(method.getName(), rhs) and - bind.flowsTo(rhs) and - bind.getMethodName() = "bind" + thiz.getBinder().getAstNode() = bindingMethod.getBody() | + // require("auto-bind")(this) + thiz.flowsTo(DataFlow::moduleImport("auto-bind").getACall().getArgument(0)) + or + 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 + 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) + ) + ) ) ) + 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/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..ecb02733337a --- /dev/null +++ b/javascript/ql/src/RegExp/IdentityReplacement.ql @@ -0,0 +1,68 @@ +/** + * @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 (RegExpLiteral rl | + rl = e and + not rl.isIgnoreCase() and + regExpMatchesString(rl.getRoot(), s) + ) + or + s = e.getStringValue() +} + +/** + * Holds if `t` matches `s` and nothing else. + */ +language[monotonicAggregates] +predicate regExpMatchesString(RegExpTerm t, string s) { + // constants match themselves + s = t.(RegExpConstant).getValue() + or + // 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 + 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 MethodCallExpr repl, string s, string friendly +where repl.getMethodName() = "replace" and + matchesString(repl.getArgument(0), s) and + 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." 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/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..226cdced23d3 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXss.js @@ -0,0 +1,14 @@ +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 => { + // 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 new file mode 100644 index 000000000000..0a05c3a7d452 --- /dev/null +++ b/javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js @@ -0,0 +1,15 @@ +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 => { + // GOOD: escaped `fileName` can not contain HTML elements + list += '
    • ' + escape(fileName) '
    • '; + }); + list += '
    ' + res.send(list); + }); +}); diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 9aec09aa6b67..9a1a3e1aa883 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 + exists(result.getOptionArgument(0, "csrf")) + or + callee = DataFlow::moduleMember("lusca", "csrf") ) } 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 diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index 16bdff91f76a..b5fe3fe5b20e 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 @@ -59,6 +60,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/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 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + 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/StringConcatenation.qll b/javascript/ql/src/semmle/javascript/StringConcatenation.qll new file mode 100644 index 000000000000..50e06860bd06 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/StringConcatenation.qll @@ -0,0 +1,80 @@ +/** + * 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, 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`. */ + 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 671729b9b2f3..1e6e47d254d9 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -185,38 +185,15 @@ 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 - ) - or - // `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() + 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 - // `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 + // awaiting a tainted expression gives a tainted result + e.(AwaitExpr).getOperand() = f ) or // reading from a tainted object yields a tainted result @@ -233,6 +210,61 @@ module TaintTracking { } } + /** + * A taint propagating data flow edge caused by the builtin array functions. + */ + private class ArrayFunctionTaintStep extends AdditionalTaintStep { + DataFlow::CallNode call; + + ArrayFunctionTaintStep() { + this = call + } + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + // `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)`, `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 + } + + } + /** * A taint propagating data flow edge for assignments of the form `o[k] = v`, where * `k` is not a constant and `o` refers to some object literal; in this case, we consider @@ -326,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/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() { 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/src/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll index ba4dc9731e7f..4328d8fdee44 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll @@ -336,17 +336,26 @@ 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` 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" - ) + this = fsModuleMember(methodName).getACall() } override DataFlow::Node getAPathArgument() { @@ -356,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`. */ 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/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/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/Lines/.gitattributes b/javascript/ql/test/library-tests/Lines/.gitattributes new file mode 100644 index 000000000000..3ca67868c632 --- /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/library-tests/StringConcatenation/ContainsTwo.expected b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected new file mode 100644 index 000000000000..841e0fad9e32 --- /dev/null +++ b/javascript/ql/test/library-tests/StringConcatenation/ContainsTwo.expected @@ -0,0 +1,31 @@ +| 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: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" | +| 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(); +} 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 + } 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 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 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..674fd74caee2 --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.expected @@ -0,0 +1,5 @@ +| 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 new file mode 100644 index 000000000000..5e1d0e95fb43 --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.js @@ -0,0 +1,52 @@ +const {BrowserWindow} = require('electron') + +function test() { + var unsafe_1 = { // NOT OK, both enabled + webPreferences: { + nodeIntegration: true, + nodeIntegrationInWorker: true, + plugins: true, + webSecurity: true, + sandbox: true + } + }; + + var options_1 = { // NOT OK, `nodeIntegrationInWorker` enabled + webPreferences: { + plugins: true, + nodeIntegrationInWorker: false, + webSecurity: true, + sandbox: true + } + }; + + var pref = { // NOT OK, implicitly enabled + plugins: true, + webSecurity: true, + sandbox: true + }; + + var options_2 = { // NOT OK, implicitly enabled + webPreferences: pref, + show: true, + frame: true, + minWidth: 300, + minHeight: 300 + }; + + var safe_used = { // NOT OK, explicitly disabled + 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}}); // 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 new file mode 100644 index 000000000000..b0315fd89ad5 --- /dev/null +++ b/javascript/ql/test/query-tests/Electron/NodeIntegration/EnablingNodeIntegration.qlref @@ -0,0 +1 @@ +Electron/EnablingNodeIntegration.ql diff --git a/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected b/javascript/ql/test/query-tests/Expressions/UnboundEventHandlerReceiver/UnboundEventHandlerReceiver.expected index d0cba5116dc7..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: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: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 196fcea7820d..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,46 @@ import React from 'react'; +import autoBind from 'auto-bind'; + +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; + return
    +
    // NOT OK +
    // NOT OK +
    // NOT OK +
    // OK +
    // OK +
    // OK +
    // OK +
    this.unbound_butInvokedSafely(e)}/> // OK +
    // OK +
    // OK +
    // OK +
    // OK +
    // OK +
    // OK +
    + } -class Component extends React.Component { constructor(props) { super(props); this.bound_throughBindInConstructor = this.bound_throughBindInConstructor.bind(this); @@ -8,6 +48,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 +92,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 +100,24 @@ class Component extends React.Component { this.setState({ }); } + bound_throughBindAllInConstructor1() { + this.setState({ }); + } + + bound_throughBindAllInConstructor2() { + this.setState({ }); + } + + @autobind + bound_throughDecorator_autobind() { + this.setState({ }); + } + + @action.bound + bound_throughDecorator_actionBound() { + this.setState({ }); + } + } // semmle-extractor-options: --experimental 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..e707ae9ecdd2 --- /dev/null +++ b/javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected @@ -0,0 +1,12 @@ +| 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. | +| 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 | /(?' + 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 + + }); +}); 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..6e063b6454b6 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,5 @@ | 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: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 new file mode 100644 index 000000000000..ee91d060dd1a --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-352/lusca_example.js @@ -0,0 +1,29 @@ +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_unsafe', parseForm, function (req, res) { // NOT OK + res.send('data is being processed') +}) 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..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,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: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 | 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('')); +});