Merge pull request #233 from Semmle/qlucie/master

Master-to-next merge
This commit is contained in:
Aditya Sharad
2018-09-27 11:50:07 +01:00
committed by GitHub
402 changed files with 13308 additions and 8078 deletions

View File

@@ -1,2 +1,2 @@
[*.{ql,qll,qlref,dbscheme,qhelp}]
end_of_line = lf
[*]
end_of_line = lf

57
.gitattributes vendored
View File

@@ -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
View File

@@ -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

View File

@@ -1,2 +1,3 @@
/csharp/ @Semmle/cs
/java/ @Semmle/java
/javascript/ @Semmle/js

View 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.

View File

@@ -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

View File

@@ -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"
]
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}
};

View File

@@ -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;
};

View File

@@ -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)
{
// ...
}

View File

@@ -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);

View File

@@ -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()
...
}
}

View File

@@ -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
...
}
}

View File

@@ -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
...
}
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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]);
}
}

View File

@@ -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
};

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View 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;
}

View File

@@ -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 */
}
}

View File

@@ -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
}
};

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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();
}

View File

@@ -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
...
}

View File

@@ -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
...
}

View File

@@ -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
}

View File

@@ -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'

View File

@@ -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[]'

View File

@@ -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'

View File

@@ -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
}

View File

@@ -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)));
}

View File

@@ -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
...
}

View File

@@ -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
...
}

View File

@@ -1,4 +1,4 @@
{
int foo = 1;
... //foo is unused
}
{
int foo = 1;
... //foo is unused
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
)
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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)
//...
}

View File

@@ -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) {
...
}
}

View File

@@ -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.

View File

@@ -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'
// ...
}

View File

@@ -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
}
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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 *)
}

View File

@@ -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
}
}
}

View File

@@ -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.
}

View File

@@ -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
}
}

View File

@@ -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]);
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -0,0 +1,7 @@
LPMALLOC pMalloc;
HRESULT hr = CoGetMalloc(1, &pMalloc);
if (!hr)
{
// code ...
}

View File

@@ -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>

View 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

View File

@@ -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. " +

View File

@@ -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
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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()

View File

@@ -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)
}
}

View File

@@ -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.

View File

@@ -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>

View File

@@ -0,0 +1 @@
import implementation.aliased_ssa.PrintIR

View File

@@ -0,0 +1 @@
import implementation.aliased_ssa.gvn.ValueNumbering

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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())
)
)
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR

View File

@@ -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))
}

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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())
)
)
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.raw.IR as IR

View File

@@ -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)

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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())
)
)
}

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR

View File

@@ -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))
}

File diff suppressed because it is too large Load Diff

View File

@@ -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()

View File

@@ -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>

View File

@@ -1,5 +1,5 @@
// common.h
#include "nameclash.h"
// common.h
#include "nameclash.h"
static int myArray[sizeof(MYTYPE)];

View File

@@ -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