mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
@@ -1,2 +1,2 @@
|
||||
[*.{ql,qll,qlref,dbscheme,qhelp}]
|
||||
end_of_line = lf
|
||||
[*]
|
||||
end_of_line = lf
|
||||
|
||||
57
.gitattributes
vendored
57
.gitattributes
vendored
@@ -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
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
/csharp/ @Semmle/cs
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
|
||||
20
change-notes/1.19/analysis-cpp.md
Normal file
20
change-notes/1.19/analysis-cpp.md
Normal file
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <stdio.h>
|
||||
#include <myfile.h> // 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 <stdio.h>
|
||||
#include <myfile.h> // 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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()
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
void f() {
|
||||
static int i = 0; //i is unused
|
||||
...
|
||||
return;
|
||||
}
|
||||
void f() {
|
||||
static int i = 0; //i is unused
|
||||
...
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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[]'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
|
||||
@@ -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
|
||||
...
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
int foo = 1;
|
||||
... //foo is unused
|
||||
}
|
||||
{
|
||||
int foo = 1;
|
||||
... //foo is unused
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
//...
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 *)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
LPMALLOC pMalloc;
|
||||
HRESULT hr = CoGetMalloc(1, &pMalloc);
|
||||
|
||||
if (!hr)
|
||||
{
|
||||
// code ...
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>This query indicates that an <code>HRESULT</code> is being cast to a boolean type or vice versa.</p>
|
||||
<p>The typical success value (<code>S_OK</code>) of an <code>HRESULT</code> equals 0. However, 0 indicates failure for a boolean type.</p>
|
||||
<p>Casting an <code>HRESULT</code> to a boolean type and then using it in a test expression will yield an incorrect result.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>To check if a call that returns an HRESULT succeeded use the <code>FAILED</code> macro.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, <code>HRESULT</code> is used in a test expression incorrectly as it may yield an incorrect result.</p>
|
||||
<sample src="HResultBooleanConversion.cpp" />
|
||||
|
||||
<p>To fix this issue, use the <code>FAILED</code> macro in the test expression.</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
71
cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
Normal file
71
cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
Normal file
@@ -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
|
||||
@@ -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. " +
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>This query indicates that a call is setting the DACL field in a <code>SECURITY_DESCRIPTOR</code> to null.</p>
|
||||
<p>When using <code>SetSecurityDescriptorDacl</code> to set a discretionary access control (DACL), setting the <code>bDaclPresent</code> argument to <code>TRUE</code> indicates the prescence of a DACL in the security description in the argument <code>pDacl</code>.</p>
|
||||
<p>When the <code>pDacl</code> parameter does not point to a DACL (i.e. it is <code>NULL</code>) and the <code>bDaclPresent</code> flag is <code>TRUE</code>, a <code>NULL DACL</code> is specified.</p>
|
||||
<p>A <code>NULL DACL</code> grants full access to any user who requests it; normal security checking is not performed with respect to the object.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>You should not use a <code>NULL DACL</code> with an object because any user can change the DACL and owner of the security descriptor.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the call to <code>SetSecurityDescriptorDacl</code> is setting an unsafe DACL (<code>NULL DACL</code>) to the security descriptor.</p>
|
||||
<sample src="UnsafeDaclSecurityDescriptor.cpp" />
|
||||
|
||||
<p>To fix this issue, <code>pDacl</code> argument should be a pointer to an <code>ACL</code> structure that specifies the DACL for the security descriptor.</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-setsecuritydescriptordacl">SetSecurityDescriptorDacl function (Microsoft documentation).</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
/**
|
||||
* 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)
|
||||
)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
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")
|
||||
)
|
||||
acquireExpr(acquireAssign.getRValue(), kind)
|
||||
}
|
||||
|
||||
private Assignment getANew() {
|
||||
result.getLValue() = this.getAnAccess() and
|
||||
(result.getRValue() instanceof NewExpr or result.getRValue() instanceof NewArrayExpr) and
|
||||
this.inSameClass(result)
|
||||
}
|
||||
|
||||
Expr getAReleaseExpr(string releaseName) {
|
||||
exprReleases(result, this.getAnAccess(), releaseName)
|
||||
Expr getAReleaseExpr(string kind) {
|
||||
exprReleases(result, this.getAnAccess(), kind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="semmlecode-cpp-queries"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.library"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.dbscheme"/>
|
||||
<path value="/semmlecode.cpp.dbscheme"/>
|
||||
</extension>
|
||||
</plugin>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="semmlecode-cpp-queries"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.library"/>
|
||||
</extension>
|
||||
|
||||
<extension point="com.semmle.plugin.qdt.ui.resources">
|
||||
<name value="com.semmle.code.cpp.dbscheme"/>
|
||||
<path value="/semmlecode.cpp.dbscheme"/>
|
||||
</extension>
|
||||
</plugin>
|
||||
|
||||
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.aliased_ssa.PrintIR
|
||||
1
cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll
Normal file
1
cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.aliased_ssa.gvn.ValueNumbering
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
1095
cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
Normal file
1095
cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>semmlecode-cpp-tests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.semmle.plugin.qdt.core.qlnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>semmlecode-cpp-tests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.semmle.plugin.qdt.core.qlnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// common.h
|
||||
|
||||
#include "nameclash.h"
|
||||
|
||||
// common.h
|
||||
|
||||
#include "nameclash.h"
|
||||
|
||||
static int myArray[sizeof(MYTYPE)];
|
||||
@@ -1,3 +1,3 @@
|
||||
// defines_issue.h
|
||||
|
||||
// defines_issue.h
|
||||
|
||||
#include DEFINED_HEADER
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user