Merge branch 'master' into hresult-boolean-qhelp

This commit is contained in:
Jonas Jensen
2018-10-09 14:56:58 +02:00
committed by GitHub
235 changed files with 9642 additions and 1188 deletions

7
.gitignore vendored
View File

@@ -8,8 +8,7 @@
# qltest projects and artifacts
*/ql/test/**/*.testproj
*/ql/test/**/*.actual
/.vs/slnx.sqlite
/.vs/ql/v15/Browse.VC.opendb
/.vs/ql/v15/Browse.VC.db
/.vs/ProjectSettings.json
# Visual studio temporaries, except a file used by QL4VS
.vs/*
!.vs/VSWorkspaceSettings.json

View File

@@ -8,13 +8,17 @@
|-----------------------------|-----------|--------------------------------------------------------------------|
| Cast between HRESULT and a Boolean type (`cpp/hresult-boolean-conversion`) | external/cwe/cwe-253 | Finds logic errors caused by mistakenly treating the Windows `HRESULT` type as a Boolean instead of testing it with the appropriate macros. Enabled by default. |
| Setting a DACL to `NULL` in a `SECURITY_DESCRIPTOR` (`cpp/unsafe-dacl-security-descriptor`) | external/cwe/cwe-732 | This query finds code that creates world-writable objects on Windows by setting their DACL to `NULL`. Enabled by default. |
| Cast from char* to wchar_t* | security, external/cwe/cwe-704 | Detects potentially dangerous casts from char* to wchar_t*. Enabled by default on LGTM. |
## 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. |
| Missing return statement (`cpp/missing-return`) | Visible by default | The precision of this query has been increased from 'medium' to 'high', which makes it visible by default in LGTM. It was 'medium' in release 1.17 and 1.18 because it had false positives due to an extractor bug that was fixed in 1.18. |
| Call to memory access function may overflow buffer | More correct results | Array indexing with a negative index is now detected by this query. |
| Suspicious add with sizeof | Fewer false positive results | Arithmetic with void pointers (where allowed) is now excluded from this query. |
| Wrong type of arguments to formatting function | Fewer false positive results | False positive results involving typedefs have been removed. Expected argument types are determined more accurately, especially for wide string and pointer types. Custom (non-standard) formatting functions are also identified more accurately. |
## Changes to QL libraries

View File

@@ -9,22 +9,30 @@
* 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)
* The type inference now handles nested imports (that is, imports not appearing at the toplevel). This may yield fewer false-positive results on projects that use this non-standard language feature.
## New queries
| **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. |
| Host header poisoning in email generation | security, external/cwe/cwe-640 | Highlights code that generates emails with links that can be hijacked by HTTP host header poisoning, indicating a violation of [CWE-640](https://cwe.mitre.org/data/definitions/640.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. |
| 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. |
| Unclear precedence of nested operators (`js/unclear-operator-precedence`) | maintainability, correctness, external/cwe/cwe-783 | Highlights nested binary operators whose relative precedence is easy to misunderstand. Results shown on LGTM by default. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|--------------------------------|----------------------------|----------------------------------------------|
| Useless assignment to local variable | Fewer false-positive results | This rule now recognizes additional ways default values can be set. |
| 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. |
| Server-side URL redirect | More results | This rule now recognizes redirection calls in more cases. |
| Whitespace contradicts operator precedence | Fewer false-positive results | This rule no longer flags operators with asymmetric whitespace. |
## Changes to QL libraries
* The flow configuration framework now supports distinguishing and tracking different kinds of taint, specified by an extensible class `FlowLabel` (which can also be referred to by its alias `TaintKind`).

View File

@@ -119,7 +119,7 @@ class CommentBlock extends Comment {
*/
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
this.lastComment().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
this.lastComment().getLocation().hasLocationInfo(_, _, _, endline, endcolumn)
}
}

View File

@@ -22,13 +22,15 @@ import UnsignedGEZero
// #define PRINTMSG(val,msg) { if (val >= PRINTLEVEL) printf(msg); }
//
// So to reduce the number of false positives, we do not report a result if
// the comparison is in a macro expansion.
// the comparison is in a macro expansion. Similarly for template
// instantiations.
from
ComparisonOperation cmp, SmallSide ss,
float left, float right, boolean value,
string reason
where
not cmp.isInMacroExpansion() and
not cmp.isFromTemplateInstantiation(_) and
reachablePointlessComparison(cmp, left, right, value, ss) and
// a comparison between an enum and zero is always valid because whether

View File

@@ -25,7 +25,7 @@ predicate stringType(Type t, Type charType) {
charType = t.(ArrayType).getBaseType()
) and (
charType.getUnspecifiedType() instanceof CharType or
charType.getUnspecifiedType() instanceof WideCharType
charType.getUnspecifiedType() instanceof Wchar_t
)
)
or

View File

@@ -25,7 +25,8 @@ private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc,
ffc.getTarget() = f and
f.getFormatParameterIndex() = i and
ffc.getArgument(i) = fl and
fl.getConversionType(pos) = expected
fl.getConversionType(pos) = expected and
count(fl.getConversionType(pos)) = 1
)
}
@@ -66,30 +67,15 @@ predicate formatOtherArgType(FormattingFunctionCall ffc, int pos, Type expected,
class ExpectedType extends Type
{
ExpectedType() {
formatArgType(_, _, this, _, _) or
formatOtherArgType(_, _, this, _, _) or
exists(ExpectedType t |
this = t.(PointerType).getBaseType()
exists(Type t |
(
formatArgType(_, _, t, _, _) or
formatOtherArgType(_, _, t, _, _)
) and this = t.getUnspecifiedType()
)
}
}
/**
* Gets an 'interesting' type that can be reached from `t` by removing
* typedefs and specifiers. Note that this does not always mean removing
* all typedefs and specifiers as `Type.getUnspecifiedType()` would, for
* example if the interesting type is itself a typedef.
*/
ExpectedType getAnUnderlyingExpectedType(Type t) {
(
result = t
) or (
result = getAnUnderlyingExpectedType(t.(TypedefType).getBaseType())
) or (
result = getAnUnderlyingExpectedType(t.(SpecifiedType).getBaseType())
)
}
/**
* Holds if it is safe to display a value of type `actual` when `printf`
* expects a value of type `expected`.
@@ -100,59 +86,48 @@ ExpectedType getAnUnderlyingExpectedType(Type t) {
* are converted to `double`.
*/
predicate trivialConversion(ExpectedType expected, Type actual) {
formatArgType(_, _, expected, _, actual) and
exists(Type actualU |
actualU = actual.getUnspecifiedType() and
exists(Type exp, Type act |
formatArgType(_, _, exp, _, act) and
expected = exp.getUnspecifiedType() and
actual = act.getUnspecifiedType()
) and (
(
(
// allow a pointer type to be displayed with `%p`
expected instanceof VoidPointerType and actualU instanceof PointerType
) or (
// allow a function pointer type to be displayed with `%p`
expected instanceof VoidPointerType and actualU instanceof FunctionPointerType and expected.getSize() = actual.getSize()
) or (
// allow an `enum` type to be displayed with `%i`, `%c` etc
expected instanceof IntegralType and actualU instanceof Enum
) or (
// allow any `char *` type to be displayed with `%s`
expected instanceof CharPointerType and actualU instanceof CharPointerType
) or (
// allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed
// with `%ws`
expected.(PointerType).getBaseType().hasName("wchar_t") and
exists(Wchar_t t |
actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize()
)
) or (
// allow an `int` (or anything promoted to `int`) to be displayed with `%c`
expected instanceof CharType and actualU instanceof IntType
) or (
// allow an `int` (or anything promoted to `int`) to be displayed with `%wc`
expected instanceof Wchar_t and actualU instanceof IntType
) or (
expected instanceof UnsignedCharType and actualU instanceof IntType
) or (
// allow the underlying type of a `size_t` (e.g. `unsigned long`) for
// `%zu`, since this is the type of a `sizeof` expression
expected instanceof Size_t and
actual.getUnspecifiedType() = expected.getUnspecifiedType()
) or (
// allow the underlying type of a `ssize_t` (e.g. `long`) for `%zd`
expected instanceof Ssize_t and
actual.getUnspecifiedType() = expected.getUnspecifiedType()
) or (
// allow any integral type of the same size
// (this permits signedness changes)
expected.(IntegralType).getSize() = actualU.(IntegralType).getSize()
) or (
// allow a pointer to any integral type of the same size
// (this permits signedness changes)
expected.(PointerType).getBaseType().(IntegralType).getSize() = actualU.(PointerType).getBaseType().(IntegralType).getSize()
) or (
// allow expected, or a typedef or specified version of expected
expected = getAnUnderlyingExpectedType(actual)
// allow a pointer type to be displayed with `%p`
expected instanceof VoidPointerType and actual instanceof PointerType
) or (
// allow a function pointer type to be displayed with `%p`
expected instanceof VoidPointerType and actual instanceof FunctionPointerType and expected.getSize() = actual.getSize()
) or (
// allow an `enum` type to be displayed with `%i`, `%c` etc
expected instanceof IntegralType and actual instanceof Enum
) or (
// allow any `char *` type to be displayed with `%s`
expected instanceof CharPointerType and actual instanceof CharPointerType
) or (
// allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed
// with `%ws`
expected.(PointerType).getBaseType().hasName("wchar_t") and
exists(Wchar_t t |
actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize()
)
) or (
// allow an `int` (or anything promoted to `int`) to be displayed with `%c`
expected instanceof CharType and actual instanceof IntType
) or (
// allow an `int` (or anything promoted to `int`) to be displayed with `%wc`
expected instanceof Wchar_t and actual instanceof IntType
) or (
expected instanceof UnsignedCharType and actual instanceof IntType
) or (
// allow any integral type of the same size
// (this permits signedness changes)
expected.(IntegralType).getSize() = actual.(IntegralType).getSize()
) or (
// allow a pointer to any integral type of the same size
// (this permits signedness changes)
expected.(PointerType).getBaseType().(IntegralType).getSize() = actual.(PointerType).getBaseType().(IntegralType).getSize()
) or (
expected = actual
)
)
}
@@ -164,16 +139,16 @@ int sizeof_IntType() {
exists(IntType it | result = it.getSize())
}
from FormattingFunctionCall ffc, int n, Expr arg, ExpectedType expected, Type actual
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
where (
(
formatArgType(ffc, n, expected, arg, actual) and
not trivialConversion(expected, actual)
not trivialConversion(expected.getUnspecifiedType(), actual.getUnspecifiedType())
)
or
(
formatOtherArgType(ffc, n, expected, arg, actual) and
not actual.getUnderlyingType().(IntegralType).getSize() = sizeof_IntType()
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
)
)
and not arg.isAffectedByMacro()

View File

@@ -41,11 +41,13 @@ Type stripType(Type t) {
result = stripType(t.(ArrayType).getBaseType()) or
result = stripType(t.(ReferenceType).getBaseType()) or
result = stripType(t.(SpecifiedType).getBaseType()) or
result = stripType(t.(Decltype).getBaseType()) or
(
not t instanceof TypedefType and
not t instanceof ArrayType and
not t instanceof ReferenceType and
not t instanceof SpecifiedType and
not t instanceof Decltype and
result = t
)
}

View File

@@ -11,10 +11,21 @@
import cpp
from Initializer init, Variable v, VariableAccess va
where init.getDeclaration() = v
and va.getTarget() = v
and va.getParent*() = init
class VariableAccessInInitializer extends VariableAccess {
Variable var;
Initializer init;
VariableAccessInInitializer() {
init.getDeclaration() = var and
init.getExpr().getAChild*() = this
}
predicate initializesItself(Variable v, Initializer i) {
v = var and i = init and var = this.getTarget()
}
}
from Initializer init, Variable v, VariableAccessInInitializer va
where va.initializesItself(v, init)
and (
va.hasLValueToRValueConversion() or
exists (Assignment assn | assn.getLValue() = va) or

View File

@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Semmle C/C++ Default Queries
Bundle-SymbolicName: com.semmle.plugin.semmlecode.cpp.queries;singleton:=true
Bundle-Version: 1.18.0.qualifier
Bundle-Version: 1.18.1.qualifier
Bundle-Vendor: Semmle Ltd.
Bundle-ActivationPolicy: lazy
Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.0.qualifier,1.18.0.qualifier]"
Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.1.qualifier,1.18.1.qualifier]"

View File

@@ -19,7 +19,7 @@ import cpp
class AnyCharPointerType extends PointerType {
AnyCharPointerType() {
this.getBaseType().getUnderlyingType() instanceof CharType or
this.getBaseType().getUnderlyingType() instanceof WideCharType
this.getBaseType().getUnderlyingType() instanceof Wchar_t
}
}
@@ -29,7 +29,7 @@ class AnyCharPointerType extends PointerType {
class AnyCharArrayType extends ArrayType {
AnyCharArrayType() {
this.getBaseType().getUnderlyingType() instanceof CharType or
this.getBaseType().getUnderlyingType() instanceof WideCharType
this.getBaseType().getUnderlyingType() instanceof Wchar_t
}
}

View File

@@ -0,0 +1,3 @@
wchar_t* pSrc;
pSrc = (wchar_t*)"a"; // casting a byte-string literal "a" to a wide-character string

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule indicates a potentially incorrect cast from an byte string (<code>char *</code>) to a wide-character string (<code>wchar_t *</code>).</p>
<p>This cast might yield strings that are not correctly terminated; including potential buffer overruns when using such strings with some dangerous APIs.</p>
</overview>
<recommendation>
<p>Do not explicitly cast byte strings to wide-character strings.</p>
<p>For string literals, prepend the literal string with the letter "L" to indicate that the string is a wide-character string (<code>wchar_t *</code>).</p>
<p>For converting a byte literal to a wide-character string literal, you would need to use the appropriate conversion function for the platform you are using. Please see the references section for options according to your platform.</p>
</recommendation>
<example>
<p>In the following example, an byte string literal (<code>"a"</code>) is cast to a wide-character string.</p>
<sample src="WcharCharConversion.cpp" />
<p>To fix this issue, prepend the literal with the letter "L" (<code>L"a"</code>) to define it as a wide-character string.</p>
</example>
<references>
<li>
General resources:
<a href="https://en.cppreference.com/w/cpp/string/multibyte/mbstowcs">std::mbstowcs</a>
</li>
<li>
Microsoft specific resources:
<a href="https://docs.microsoft.com/en-us/windows/desktop/Intl/security-considerations--international-features">Security Considerations: International Features</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,29 @@
/**
* @name Cast from char* to wchar_t*
* @description Casting a byte string to a wide-character string is likely
* to yield a string that is incorrectly terminated or aligned.
* This can lead to undefined behavior, including buffer overruns.
* @kind problem
* @id cpp/incorrect-string-type-conversion
* @problem.severity error
* @precision high
* @tags security
* external/cwe/cwe-704
* external/microsoft/c/c6276
*/
import cpp
class WideCharPointerType extends PointerType {
WideCharPointerType() {
this.getBaseType() instanceof WideCharType
}
}
from Expr e1, Cast e2
where
e2 = e1.getConversion() and
exists(WideCharPointerType w, CharPointerType c |
w = e2.getType().getUnspecifiedType().(PointerType) and
c = e1.getType().getUnspecifiedType().(PointerType)
)
select e1, "Conversion from " + e1.getType().toString() + " to " + e2.getType().toString() + ". Use of invalid string can lead to undefined behavior."

View File

@@ -3,7 +3,7 @@
* @description All functions that are not void should return a value on every exit path.
* @kind problem
* @problem.severity error
* @precision medium
* @precision high
* @id cpp/missing-return
* @tags reliability
* readability

View File

@@ -2,41 +2,38 @@ import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
/**
* Get the `@element` that represents this `@element`.
* Normally this will simply be `e`, but sometimes it is not.
* For example, for an incomplete struct `e` the result may be a
* complete struct with the same name.
*/
private cached @element resolveElement(@element e) {
if isClass(e)
then result = resolveClass(e)
else result = e
}
/**
* Get the `Element` that represents this `@element`.
* Normally this will simply be a cast of `e`, but sometimes it is not.
* For example, for an incomplete struct `e` the result may be a
* complete struct with the same name.
*/
pragma[inline]
Element mkElement(@element e) {
result = resolveElement(e)
unresolveElement(result) = e
}
/**
* Get an `@element` that resolves to the `Element`. This should
* INTERNAL: Do not use.
*
* Gets an `@element` that resolves to the `Element`. This should
* normally only be called from member predicates, where `e` is not
* `this` and you need the result for an argument to a database
* extensional.
* See `underlyingElement` for when `e` is `this`.
*/
pragma[inline]
@element unresolveElement(Element e) {
resolveElement(result) = e
not result instanceof @usertype and
result = e
or
e = resolveClass(result)
}
/**
* Get the `@element` that this `Element` extends. This should normally
* INTERNAL: Do not use.
*
* Gets the `@element` that this `Element` extends. This should normally
* only be called from member predicates, where `e` is `this` and you
* need the result for an argument to a database extensional.
* See `unresolveElement` for when `e` is not `this`.
@@ -53,10 +50,6 @@ Element mkElement(@element e) {
* `getLocation`, or `hasLocationInfo`.
*/
class ElementBase extends @element {
ElementBase() {
this = resolveElement(_)
}
/** Gets a textual representation of this element. */
string toString() { none() }
}

View File

@@ -294,13 +294,13 @@ class AttributeArgument extends Element, @attribute_arg {
}
override string toString() {
if exists (@attribute_arg_empty self | mkElement(self) = this)
if exists (@attribute_arg_empty self | self = underlyingElement(this))
then result = "empty argument"
else exists (string prefix, string tail
| (if exists(getName())
then prefix = getName() + "="
else prefix = "") and
(if exists (@attribute_arg_type self | mkElement(self) = this)
(if exists (@attribute_arg_type self | self = underlyingElement(this))
then tail = getValueType().getName()
else tail = getValueText()) and
result = prefix + tail)

View File

@@ -7,6 +7,8 @@ private import semmle.code.cpp.internal.ResolveClass
* A C/C++ type.
*/
class Type extends Locatable, @type {
Type() { isType(underlyingElement(this)) }
/**
* Gets the name of this type.
*/
@@ -599,6 +601,10 @@ class VoidType extends BuiltInType {
/**
* The C/C++ wide character type.
*
* Note that on some platforms `wchar_t` doesn't exist as a built-in
* type but a typedef is provided. Consider using the `Wchar_t` QL
* class to include these types.
*/
class WideCharType extends IntegralType {

View File

@@ -1,16 +1,16 @@
/**
* A library for working with XML files and their content.
* Provides classes and predicates for working with XML files and their content.
*/
import semmle.code.cpp.Location
/** An XML element that has a location. */
abstract class XMLLocatable extends @xmllocatable {
/** The source location for this element. */
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this,result) }
/**
* Whether this element has the specified location information,
* Holds if this element has the specified location information,
* including file path, start line, start column, end line and end column.
*/
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
@@ -20,7 +20,7 @@ abstract class XMLLocatable extends @xmllocatable {
)
}
/** A printable representation of this element. */
/** Gets a printable representation of this element. */
abstract string toString();
}
@@ -30,46 +30,49 @@ abstract class XMLLocatable extends @xmllocatable {
*/
class XMLParent extends @xmlparent {
/**
* A printable representation of this XML parent.
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)
*/
abstract string getName();
/** The file to which this XML parent belongs. */
/** Gets the file to which this XML parent belongs. */
XMLFile getFile() { result = this or xmlElements(this,_,_,_,result) }
/** The child element at a specified index of this XML parent. */
/** Gets the child element at a specified index of this XML parent. */
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
/** A child element of this XML parent. */
/** Gets a child element of this XML parent. */
XMLElement getAChild() { xmlElements(result,_,this,_,_) }
/** A child element of this XML parent with the given `name`. */
/** Gets a child element of this XML parent with the given `name`. */
XMLElement getAChild(string name) { xmlElements(result,_,this,_,_) and result.hasName(name) }
/** A comment that is a child of this XML parent. */
/** Gets a comment that is a child of this XML parent. */
XMLComment getAComment() { xmlComments(result,_,this,_) }
/** A character sequence that is a child of this XML parent. */
/** Gets a character sequence that is a child of this XML parent. */
XMLCharacters getACharactersSet() { xmlChars(result,_,this,_,_,_) }
/** The depth in the tree. (Overridden in XMLElement.) */
/** Gets the depth in the tree. (Overridden in XMLElement.) */
int getDepth() { result = 0 }
/** The number of child XML elements of this XML parent. */
/** Gets the number of child XML elements of this XML parent. */
int getNumberOfChildren() {
result = count(XMLElement e | xmlElements(e,_,this,_,_))
}
/** The number of places in the body of this XML parent where text occurs. */
/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() {
result = count(int pos | xmlChars(_,_,this,pos,_,_))
}
/**
* DEPRECATED: Internal.
*
* Append the character sequences of this XML parent from left to right, separated by a space,
* up to a specified (zero-based) index.
*/
deprecated
string charsSetUpTo(int n) {
(n = 0 and xmlChars(_,result,this,0,_,_)) or
(n > 0 and exists(string chars | xmlChars(_,chars,this,n,_,_) |
@@ -78,18 +81,15 @@ class XMLParent extends @xmlparent {
/** Append all the character sequences of this XML parent from left to right, separated by a space. */
string allCharactersString() {
exists(int n | n = this.getNumberOfCharacterSets() |
(n = 0 and result = "") or
(n > 0 and result = this.charsSetUpTo(n-1))
)
result = concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
}
/** The text value contained in this XML parent. */
/** Gets the text value contained in this XML parent. */
string getTextValue() {
result = allCharactersString()
}
/** A printable representation of this XML parent. */
/** Gets a printable representation of this XML parent. */
string toString() { result = this.getName() }
}
@@ -99,54 +99,54 @@ class XMLFile extends XMLParent, File {
xmlEncoding(this,_)
}
/** A printable representation of this XML file. */
/** Gets a printable representation of this XML file. */
override
string toString() { result = XMLParent.super.toString() }
/** The name of this XML file. */
/** Gets the name of this XML file. */
override
string getName() { files(this,result,_,_,_) }
/** The path of this XML file. */
/** Gets the path of this XML file. */
string getPath() { files(this,_,result,_,_) }
/** The path of the folder that contains this XML file. */
/** Gets the path of the folder that contains this XML file. */
string getFolder() {
result = this.getPath().substring(0, this.getPath().length()-this.getName().length())
}
/** The encoding of this XML file. */
/** Gets the encoding of this XML file. */
string getEncoding() { xmlEncoding(this,result) }
/** The XML file itself. */
/** Gets the XML file itself. */
override
XMLFile getFile() { result = this }
/** A top-most element in an XML file. */
/** Gets a top-most element in an XML file. */
XMLElement getARootElement() { result = this.getAChild() }
/** A DTD associated with this XML file. */
/** Gets a DTD associated with this XML file. */
XMLDTD getADTD() { xmlDTDs(result,_,_,_,this) }
}
/** A "Document Type Definition" of an XML file. */
class XMLDTD extends @xmldtd {
/** The name of the root element of this DTD. */
/** Gets the name of the root element of this DTD. */
string getRoot() { xmlDTDs(this,result,_,_,_) }
/** The public ID of this DTD. */
/** Gets the public ID of this DTD. */
string getPublicId() { xmlDTDs(this,_,result,_,_) }
/** The system ID of this DTD. */
/** Gets the system ID of this DTD. */
string getSystemId() { xmlDTDs(this,_,_,result,_) }
/** Whether this DTD is public. */
/** Holds if this DTD is public. */
predicate isPublic() { not xmlDTDs(this,_,"",_,_) }
/** The parent of this DTD. */
/** Gets the parent of this DTD. */
XMLParent getParent() { xmlDTDs(this,_,_,_,result) }
/** A printable representation of this DTD. */
/** Gets a printable representation of this DTD. */
string toString() {
(this.isPublic() and result = this.getRoot() + " PUBLIC '" +
this.getPublicId() + "' '" +
@@ -159,92 +159,92 @@ class XMLDTD extends @xmldtd {
/** An XML tag in an XML file. */
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
/** Whether this XML element has the given `name`. */
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = getName() }
/** The name of this XML element. */
/** Gets the name of this XML element. */
override
string getName() { xmlElements(this,result,_,_,_) }
/** The XML file in which this XML element occurs. */
/** Gets the XML file in which this XML element occurs. */
override
XMLFile getFile() { xmlElements(this,_,_,_,result) }
/** The parent of this XML element. */
/** Gets the parent of this XML element. */
XMLParent getParent() { xmlElements(this,_,result,_,_) }
/** The index of this XML element among its parent's children. */
/** Gets the index of this XML element among its parent's children. */
int getIndex() { xmlElements(this, _, _, result, _) }
/** Whether this XML element has a namespace. */
/** Holds if this XML element has a namespace. */
predicate hasNamespace() { xmlHasNs(this,_,_) }
/** The namespace of this XML element, if any. */
/** Gets the namespace of this XML element, if any. */
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
/** The index of this XML element among its parent's children. */
/** Gets the index of this XML element among its parent's children. */
int getElementPositionIndex() { xmlElements(this,_,_,result,_) }
/** The depth of this element within the XML file tree structure. */
/** Gets the depth of this element within the XML file tree structure. */
override
int getDepth() { result = this.getParent().getDepth() + 1 }
/** An XML attribute of this XML element. */
/** Gets an XML attribute of this XML element. */
XMLAttribute getAnAttribute() { result.getElement() = this }
/** The attribute with the specified `name`, if any. */
/** Gets the attribute with the specified `name`, if any. */
XMLAttribute getAttribute(string name) {
result.getElement() = this and result.getName() = name
}
/** Whether this XML element has an attribute with the specified `name`. */
/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) {
exists(XMLAttribute a| a = this.getAttribute(name))
}
/** The value of the attribute with the specified `name`, if any. */
/** Gets the value of the attribute with the specified `name`, if any. */
string getAttributeValue(string name) {
result = this.getAttribute(name).getValue()
}
/** A printable representation of this XML element. */
/** Gets a printable representation of this XML element. */
override
string toString() { result = XMLParent.super.toString() }
}
/** An attribute that occurs inside an XML element. */
class XMLAttribute extends @xmlattribute, XMLLocatable {
/** The name of this attribute. */
/** Gets the name of this attribute. */
string getName() { xmlAttrs(this,_,result,_,_,_) }
/** The XML element to which this attribute belongs. */
/** Gets the XML element to which this attribute belongs. */
XMLElement getElement() { xmlAttrs(this,result,_,_,_,_) }
/** Whether this attribute has a namespace. */
/** Holds if this attribute has a namespace. */
predicate hasNamespace() { xmlHasNs(this,_,_) }
/** The namespace of this attribute, if any. */
/** Gets the namespace of this attribute, if any. */
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
/** The value of this attribute. */
/** Gets the value of this attribute. */
string getValue() { xmlAttrs(this,_,_,result,_,_) }
/** A printable representation of this XML attribute. */
/** Gets a printable representation of this XML attribute. */
override string toString() { result = this.getName() + "=" + this.getValue() }
}
/** A namespace used in an XML file */
class XMLNamespace extends @xmlnamespace {
/** The prefix of this namespace. */
/** Gets the prefix of this namespace. */
string getPrefix() { xmlNs(this,result,_,_) }
/** The URI of this namespace. */
/** Gets the URI of this namespace. */
string getURI() { xmlNs(this,_,result,_) }
/** Whether this namespace has no prefix. */
/** Holds if this namespace has no prefix. */
predicate isDefault() { this.getPrefix() = "" }
/** A printable representation of this XML namespace. */
/** Gets a printable representation of this XML namespace. */
string toString() {
(this.isDefault() and result = this.getURI()) or
(not this.isDefault() and result = this.getPrefix() + ":" + this.getURI())
@@ -253,13 +253,13 @@ class XMLNamespace extends @xmlnamespace {
/** A comment of the form `<!-- ... -->` is an XML comment. */
class XMLComment extends @xmlcomment, XMLLocatable {
/** The text content of this XML comment. */
/** Gets the text content of this XML comment. */
string getText() { xmlComments(this,result,_,_) }
/** The parent of this XML comment. */
/** Gets the parent of this XML comment. */
XMLParent getParent() { xmlComments(this,_,result,_) }
/** A printable representation of this XML comment. */
/** Gets a printable representation of this XML comment. */
override string toString() { result = this.getText() }
}
@@ -268,15 +268,15 @@ class XMLComment extends @xmlcomment, XMLLocatable {
* closing tags of an XML element, excluding other elements.
*/
class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** The content of this character sequence. */
/** Gets the content of this character sequence. */
string getCharacters() { xmlChars(this,result,_,_,_,_) }
/** The parent of this character sequence. */
/** Gets the parent of this character sequence. */
XMLParent getParent() { xmlChars(this,_,result,_,_,_) }
/** Whether this character sequence is CDATA. */
/** Holds if this character sequence is CDATA. */
predicate isCDATA() { xmlChars(this,_,_,_,1,_) }
/** A printable representation of this XML character sequence. */
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}

View File

@@ -1,7 +1,7 @@
import semmle.code.cpp.Type
/**
* The C/C++ char* type.
* The C/C++ `char*` type.
*/
class CharPointerType extends PointerType {
@@ -10,7 +10,7 @@ class CharPointerType extends PointerType {
}
/**
* The C/C++ int* type.
* The C/C++ `int*` type.
*/
class IntPointerType extends PointerType {
@@ -20,7 +20,7 @@ class IntPointerType extends PointerType {
/**
* The C/C++ void* type.
* The C/C++ `void*` type.
*/
class VoidPointerType extends PointerType {
@@ -29,7 +29,7 @@ class VoidPointerType extends PointerType {
}
/**
* The C/C++ size_t type.
* The C/C++ `size_t` type.
*/
class Size_t extends Type {
Size_t() {
@@ -39,7 +39,7 @@ class Size_t extends Type {
}
/**
* The C/C++ ssize_t type.
* The C/C++ `ssize_t` type.
*/
class Ssize_t extends Type {
Ssize_t() {
@@ -49,7 +49,7 @@ class Ssize_t extends Type {
}
/**
* The C/C++ ptrdiff_t type.
* The C/C++ `ptrdiff_t` type.
*/
class Ptrdiff_t extends Type {
Ptrdiff_t() {
@@ -59,7 +59,7 @@ class Ptrdiff_t extends Type {
}
/**
* The C/C++ intmax_t type.
* The C/C++ `intmax_t` type.
*/
class Intmax_t extends Type {
Intmax_t() {
@@ -69,7 +69,7 @@ class Intmax_t extends Type {
}
/**
* The C/C++ uintmax_t type.
* The C/C++ `uintmax_t` type.
*/
class Uintmax_t extends Type {
Uintmax_t() {
@@ -79,7 +79,11 @@ class Uintmax_t extends Type {
}
/**
* The C/C++ wchar_t type.
* The C/C++ `wchar_t` type.
*
* Note that on some platforms `wchar_t` doesn't exist as a built-in
* type but a typedef is provided. This QL class includes both cases
* (see also `WideCharType`).
*/
class Wchar_t extends Type {
Wchar_t() {

View File

@@ -220,6 +220,32 @@ class FormatLiteral extends Literal {
getUse().getTarget().(FormattingFunction).isWideCharDefault()
}
/**
* Gets the default character type expected for `%s` by this format literal. Typically
* `char` or `wchar_t`.
*/
Type getDefaultCharType() {
result = getUse().getTarget().(FormattingFunction).getDefaultCharType()
}
/**
* Gets the non-default character type expected for `%S` by this format literal. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
Type getNonDefaultCharType() {
result = getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
}
/**
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
* snapshots there may be multiple results where we can't tell which is correct for a
* particular function.
*/
Type getWideCharType() {
result = getUse().getTarget().(FormattingFunction).getWideCharType()
}
/**
* Holds if this `FormatLiteral` is in a context that supports
* Microsoft rules and extensions.
@@ -629,7 +655,6 @@ class FormatLiteral extends Literal {
result = getConversionType2(n) or
result = getConversionType3(n) or
result = getConversionType4(n) or
result = getConversionType5(n) or
result = getConversionType6(n) or
result = getConversionType7(n) or
result = getConversionType8(n) or
@@ -696,33 +721,35 @@ class FormatLiteral extends Literal {
}
/**
* Gets the 'effective' string type character, that is, 's' (meaning a char string) or
* 'S' (meaning a wide string).
* - in the base case this is the same as the format type character.
* - for a `wprintf` or similar function call, the meanings are reversed.
* - the size prefixes 'l'/'w' (long) and 'h' (short) override the
* type character to effectively 'S' or 's' respectively.
* Gets the string type required by the nth conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
* - the `%S` format character reverses wideness.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/
private string getEffectiveStringConversionChar(int n) {
exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "s" or conv = "S") |
(len = "l" and result = "S") or
(len = "w" and result = "S") or
(len = "h" and result = "s") or
(len != "l" and len != "w" and len != "h" and (result = "s" or result = "S") and (if isWideCharDefault() then result != conv else result = conv))
)
}
private Type getConversionType4(int n) {
exists(string cnv, CharType t | cnv = this.getEffectiveStringConversionChar(n) |
cnv="s" and t = result.(PointerType).getBaseType()
and not t.isExplicitlySigned()
and not t.isExplicitlyUnsigned()
)
}
private Type getConversionType5(int n) {
exists(string cnv | cnv = this.getEffectiveStringConversionChar(n) |
cnv="S" and result.(PointerType).getBaseType().hasName("wchar_t")
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(
(
(conv = "s" or conv = "S") and
len = "h" and
result.(PointerType).getBaseType() instanceof PlainCharType
) or (
(conv = "s" or conv = "S") and
(len = "l" or len = "w") and
result.(PointerType).getBaseType() = getWideCharType()
) or (
conv = "s" and
(len != "l" and len != "w" and len != "h") and
result.(PointerType).getBaseType() = getDefaultCharType()
) or (
conv = "S" and
(len != "l" and len != "w" and len != "h") and
result.(PointerType).getBaseType() = getNonDefaultCharType()
)
)
)
}

View File

@@ -0,0 +1,485 @@
import cpp
import semmle.code.cpp.ir.IR
/**
* A Boolean condition in the AST that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements.
*/
class GuardCondition extends Expr {
GuardCondition() {
exists(IRGuardCondition ir | this = ir.getUnconvertedResultExpression())
or
// no binary operators in the IR
exists(GuardCondition gc |
this.(BinaryLogicalOperation).getAnOperand()= gc
)
or
// the IR short-circuits if(!x)
(
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
not exists (Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | this.(NotExpr).getOperand() = ir.getAST())
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached predicate controls(BasicBlock controlled, boolean testIsTrue) {
none()
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
none()
}
/** Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
If `isLessThan = false` then this implies `left >= right + k`. */
cached predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
none()
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
none()
}
/** Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
If `areEqual = false` then this implies `left != right + k`. */
cached predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
none()
}
}
/**
* A binary logical operator in the AST that guards one or more basic blocks.
*/
private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
GuardConditionFromBinaryLogicalOperator() {
exists(GuardCondition gc |
this.(BinaryLogicalOperation).getAnOperand()= gc
)
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
exists (BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs
| this = binop and
lhs = binop.getLeftOperand() and
rhs = binop.getRightOperand() and
lhs.controls(controlled, testIsTrue) and
rhs.controls(controlled, testIsTrue))
}
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue) |
part.comparesLt(left, right, k, isLessThan, partIsTrue)
)
}
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
override predicate comparesEq(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue) |
part.comparesEq(left, right, k, isLessThan, partIsTrue)
)
}
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
comparesEq(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
}
/**
* A `!` operator in the AST that guards one or more basic blocks, and does not have a corresponding
* IR instruction.
*/
private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr {
GuardConditionFromShortCircuitNot() {
not exists (Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | getOperand() = ir.getAST())
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
}
override predicate comparesLt(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
getOperand().(GuardCondition).comparesLt(left, right, k, areEqual, testIsTrue.booleanNot())
}
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) {
getOperand().(GuardCondition).ensuresLt(left, right, k, block, testIsTrue.booleanNot())
}
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
}
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) {
getOperand().(GuardCondition).ensuresEq(left, right, k, block, testIsTrue.booleanNot())
}
}
/**
* A Boolean condition in the AST that guards one or more basic blocks and has a corresponding IR
* instruction.
*/
private class GuardConditionFromIR extends GuardCondition {
IRGuardCondition ir;
GuardConditionFromIR() {
this = ir.getUnconvertedResultExpression()
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
/* This condition must determine the flow of control; that is, this
* node must be a top-level condition. */
this.controlsBlock(controlled, testIsTrue)
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
exists(Instruction li, Instruction ri |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesLt(li, ri, k, isLessThan, testIsTrue)
)
}
/** Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
If `isLessThan = false` then this implies `left >= right + k`. */
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
exists(Instruction li, Instruction ri, boolean testIsTrue |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesLt(li, ri, k, isLessThan, testIsTrue) and
this.controls(block, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
exists(Instruction li, Instruction ri |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesEq(li, ri, k, areEqual, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
If `areEqual = false` then this implies `left != right + k`. */
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(Instruction li, Instruction ri, boolean testIsTrue |
li.getUnconvertedResultExpression() = left and
ri.getUnconvertedResultExpression() = right and
ir.comparesEq(li, ri, k, areEqual, testIsTrue)
and this.controls(block, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(IRBlock irb |
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled
)
}
}
/**
* A Boolean condition in the IR that guards one or more basic blocks. This includes
* operands of logical operators but not switch statements. Note that `&&` and `||`
* don't have an explicit representation in the IR, and therefore will not appear as
* IRGuardConditions.
*/
class IRGuardCondition extends Instruction {
IRGuardCondition() {
is_condition(this)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
*
* ```
* [ (testIsTrue) ]
* [ this ----------------succ ---- controlled ]
* [ | | ]
* [ (testIsFalse) | ------ ... ]
* [ other ]
* ```
*
* The predicate holds if all paths to `controlled` go via the `testIsTrue`
* edge of the control-flow graph. In other words, the `testIsTrue` edge
* must dominate `controlled`. This means that `controlled` must be
* dominated by both `this` and `succ` (the target of the `testIsTrue`
* edge). It also means that any other edge into `succ` must be a back-edge
* from a node which is dominated by `succ`.
*
* The short-circuit boolean operations have slightly surprising behavior
* here: because the operation itself only dominates one branch (due to
* being short-circuited) then it will only control blocks dominated by the
* true (for `&&`) or false (for `||`) branch.
*/
cached predicate controls(IRBlock controlled, boolean testIsTrue) {
/* This condition must determine the flow of control; that is, this
* node must be a top-level condition. */
this.controlsBlock(controlled, testIsTrue)
or
exists (IRGuardCondition ne
| this = ne.(LogicalNotInstruction).getOperand() and
ne.controls(controlled, testIsTrue.booleanNot()))
}
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
cached predicate comparesLt(Instruction left, Instruction right, int k, boolean isLessThan, boolean testIsTrue) {
compares_lt(this, left, right, k, isLessThan, testIsTrue)
}
/** Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
If `isLessThan = false` then this implies `left >= right + k`. */
cached predicate ensuresLt(Instruction left, Instruction right, int k, IRBlock block, boolean isLessThan) {
exists(boolean testIsTrue |
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
)
}
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
cached predicate comparesEq(Instruction left, Instruction right, int k, boolean areEqual, boolean testIsTrue) {
compares_eq(this, left, right, k, areEqual, testIsTrue)
}
/** Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
If `areEqual = false` then this implies `left != right + k`. */
cached predicate ensuresEq(Instruction left, Instruction right, int k, IRBlock block, boolean areEqual) {
exists(boolean testIsTrue |
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* entered if the value of this condition is `testIsTrue`. This helper
* predicate does not necessarily hold for binary logical operations like
* `&&` and `||`. See the detailed explanation on predicate `controls`.
*/
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
exists(IRBlock thisblock
| thisblock.getAnInstruction() = this
| exists(IRBlock succ, ConditionalBranchInstruction branch
| testIsTrue = true and succ.getFirstInstruction() = branch.getTrueSuccessor()
or
testIsTrue = false and succ.getFirstInstruction() = branch.getFalseSuccessor()
| branch.getCondition() = this and
succ.dominates(controlled) and
forall(IRBlock pred
| pred.getASuccessor() = succ
| pred = thisblock or succ.dominates(pred) or not pred.isReachableFromFunctionEntry())))
}
}
private predicate is_condition(Instruction guard) {
exists(ConditionalBranchInstruction branch|
branch.getCondition() = guard
)
or
exists(LogicalNotInstruction cond | is_condition(cond) and cond.getOperand() = guard)
}
/**
* Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`.
*
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
*/
private predicate compares_eq(Instruction test, Instruction left, Instruction right, int k, boolean areEqual, boolean testIsTrue) {
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
areEqual = true and testIsTrue = eq or areEqual = false and testIsTrue = eq.booleanNot()
)
// I think this is handled by forwarding in controlsBlock.
/* or
logical_comparison_eq(test, left, right, k, areEqual, testIsTrue) */
or
/* a == b + k => b == a - k */
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
or
complex_eq(test, left, right, k, areEqual, testIsTrue)
or
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_eq(test.(LogicalNotInstruction).getOperand(), left, right, k, areEqual, isFalse)
)
}
/** Rearrange various simple comparisons into `left == right + k` form. */
private predicate simple_comparison_eq(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean areEqual) {
left = cmp.getLeftOperand() and cmp instanceof CompareEQInstruction and right = cmp.getRightOperand() and k = 0 and areEqual = true
or
left = cmp.getLeftOperand() and cmp instanceof CompareNEInstruction and right = cmp.getRightOperand() and k = 0 and areEqual = false
}
private predicate complex_eq(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean areEqual, boolean testIsTrue) {
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
or
add_eq(cmp, left, right, k, areEqual, testIsTrue)
}
/* Simplification of inequality expressions
* Simplify conditions in the source to the canonical form l < r + k.
*/
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
private predicate compares_lt(Instruction test, Instruction left, Instruction right, int k, boolean isLt, boolean testIsTrue) {
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
or
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
or
complex_lt(test, left, right, k, isLt, testIsTrue)
or
/* (not (left < right + k)) => (left >= right + k) */
exists(boolean isGe | isLt = isGe.booleanNot() |
compares_ge(test, left, right, k, isGe, testIsTrue)
)
or
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
compares_lt(test.(LogicalNotInstruction).getOperand(), left, right, k, isLt, isFalse)
)
}
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
private predicate compares_ge(Instruction test, Instruction left, Instruction right, int k, boolean isGe, boolean testIsTrue) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
}
/** Rearrange various simple comparisons into `left < right + k` form. */
private predicate simple_comparison_lt(CompareInstruction cmp, Instruction left, Instruction right, int k) {
left = cmp.getLeftOperand() and cmp instanceof CompareLTInstruction and right = cmp.getRightOperand() and k = 0
or
left = cmp.getLeftOperand() and cmp instanceof CompareLEInstruction and right = cmp.getRightOperand() and k = 1
or
right = cmp.getLeftOperand() and cmp instanceof CompareGTInstruction and left = cmp.getRightOperand() and k = 0
or
right = cmp.getLeftOperand() and cmp instanceof CompareGEInstruction and left = cmp.getRightOperand() and k = 1
}
private predicate complex_lt(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean isLt, boolean testIsTrue) {
sub_lt(cmp, left, right, k, isLt, testIsTrue)
or
add_lt(cmp, left, right, k, isLt, testIsTrue)
}
/* left - x < right + c => left < right + (c+x)
left < (right - x) + c => left < right + (c-x) */
private predicate sub_lt(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean isLt, boolean testIsTrue) {
exists(SubInstruction lhs, int c, int x | compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
and k = c + x
)
or
exists(SubInstruction rhs, int c, int x | compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
and k = c - x
)
}
/* left + x < right + c => left < right + (c-x)
left < (right + x) + c => left < right + (c+x) */
private predicate add_lt(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean isLt, boolean testIsTrue) {
exists(AddInstruction lhs, int c, int x | compares_lt(cmp, lhs, right, c, isLt, testIsTrue) and
(left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
)
and k = c - x
)
or
exists(AddInstruction rhs, int c, int x | compares_lt(cmp, left, rhs, c, isLt, testIsTrue) and
(right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
)
and k = c + x
)
}
/* left - x == right + c => left == right + (c+x)
left == (right - x) + c => left == right + (c-x) */
private predicate sub_eq(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean areEqual, boolean testIsTrue) {
exists(SubInstruction lhs, int c, int x | compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
and k = c + x
)
or
exists(SubInstruction rhs, int c, int x | compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
and k = c - x
)
}
/* left + x == right + c => left == right + (c-x)
left == (right + x) + c => left == right + (c+x) */
private predicate add_eq(CompareInstruction cmp, Instruction left, Instruction right, int k, boolean areEqual, boolean testIsTrue) {
exists(AddInstruction lhs, int c, int x | compares_eq(cmp, lhs, right, c, areEqual, testIsTrue) and
(left = lhs.getLeftOperand() and x = int_value(lhs.getRightOperand())
or
left = lhs.getRightOperand() and x = int_value(lhs.getLeftOperand())
)
and k = c - x
)
or
exists(AddInstruction rhs, int c, int x | compares_eq(cmp, left, rhs, c, areEqual, testIsTrue) and
(right = rhs.getLeftOperand() and x = int_value(rhs.getRightOperand())
or
right = rhs.getRightOperand() and x = int_value(rhs.getLeftOperand())
)
and k = c + x
)
}
/** The int value of integer constant expression. */
private int int_value(Instruction i) {
result = i.(IntegerConstantInstruction).getValue().toInt()
}

View File

@@ -31,8 +31,11 @@ private cached module Cached {
// or the node's predecessor has more than one successor,
// then the node is the start of a new primitive basic block.
or
strictcount (Node pred, Node other
| successors_extended(pred,node) and successors_extended(pred,other)) > 1
strictcount(Node pred | successors_extended(pred, node)) > 1
or
exists(ControlFlowNode pred | successors_extended(pred, node) |
strictcount(ControlFlowNode other | successors_extended(pred, other)) > 1
)
// If the node has zero predecessors then it is the start of
// a BB. However, the C++ AST contains many nodes with zero
@@ -63,8 +66,14 @@ private cached module Cached {
/** Holds if `node` is the `pos`th control-flow node in primitive basic block `bb`. */
cached
predicate primitive_basic_block_member(Node node, PrimitiveBasicBlock bb, int pos) {
pos = getMemberIndex(node) and
member_step*(bb, node)
primitive_basic_block_entry_node(bb) and
(
pos = 0 and
node = bb
or
pos = getMemberIndex(node) and
member_step+(bb, node)
)
}
/** Gets the number of control-flow nodes in the primitive basic block `bb`. */

View File

@@ -54,4 +54,10 @@ cached private module Cached {
(usertypes(t,_,1) or usertypes(t,_,2) or usertypes(t,_,3) or usertypes(t,_,6)
or usertypes(t,_,10) or usertypes(t,_,11) or usertypes(t,_,12))
}
cached predicate isType(@type t) {
not isClass(t)
or
t = resolveClass(_)
}
}

View File

@@ -76,6 +76,8 @@ abstract class PointerOffsetOpcode extends PointerArithmeticOpcode {}
abstract class CompareOpcode extends BinaryOpcode {}
abstract class RelationalOpcode extends CompareOpcode {}
abstract class CopyOpcode extends Opcode {}
abstract class MemoryAccessOpcode extends Opcode {}
@@ -117,10 +119,10 @@ module Opcode {
class LogicalNot extends UnaryOpcode, TLogicalNot { override final string toString() { result = "LogicalNot" } }
class CompareEQ extends CompareOpcode, TCompareEQ { override final string toString() { result = "CompareEQ" } }
class CompareNE extends CompareOpcode, TCompareNE { override final string toString() { result = "CompareNE" } }
class CompareLT extends CompareOpcode, TCompareLT { override final string toString() { result = "CompareLT" } }
class CompareGT extends CompareOpcode, TCompareGT { override final string toString() { result = "CompareGT" } }
class CompareLE extends CompareOpcode, TCompareLE { override final string toString() { result = "CompareLE" } }
class CompareGE extends CompareOpcode, TCompareGE { override final string toString() { result = "CompareGE" } }
class CompareLT extends RelationalOpcode, TCompareLT { override final string toString() { result = "CompareLT" } }
class CompareGT extends RelationalOpcode, TCompareGT { override final string toString() { result = "CompareGT" } }
class CompareLE extends RelationalOpcode, TCompareLE { override final string toString() { result = "CompareLE" } }
class CompareGE extends RelationalOpcode, TCompareGE { override final string toString() { result = "CompareGE" } }
class PointerAdd extends PointerOffsetOpcode, TPointerAdd { override final string toString() { result = "PointerAdd" } }
class PointerSub extends PointerOffsetOpcode, TPointerSub { override final string toString() { result = "PointerSub" } }
class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { override final string toString() { result = "PointerDiff" } }

View File

@@ -89,4 +89,12 @@ class IRBlock extends TIRBlock {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
}
/**
* Holds if this block is reachable from the entry point of its function
*/
final predicate isReachableFromFunctionEntry() {
this = getFunctionIR().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -133,6 +133,16 @@ class Instruction extends Construction::TInstruction {
}
final string toString() {
result = getOpcode().toString() + ": " + getAST().toString()
}
/**
* Gets a string showing the result, opcode, and operands of the instruction, equivalent to what
* would be printed by PrintIR.ql. For example:
*
* `mu0_28(int) = Store r0_26, r0_27`
*/
final string getDumpString() {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
@@ -310,11 +320,19 @@ class Instruction extends Construction::TInstruction {
}
/**
* Gets the `Expr` whose results is computed by this instruction, if any.
* Gets the `Expr` whose result is computed by this instruction, if any.
*/
final Expr getResultExpression() {
result = Construction::getInstructionResultExpression(this)
final Expr getConvertedResultExpression() {
result = Construction::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted `Expr` whose result is computed by this instruction, if any.
*/
final Expr getUnconvertedResultExpression() {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
/**
* Gets the type of the result produced by this instruction. If the
@@ -967,28 +985,110 @@ class CompareNEInstruction extends CompareInstruction {
}
}
class CompareLTInstruction extends CompareInstruction {
/**
* Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() {
opcode instanceof RelationalOpcode
}
/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational instruction, that is, the side that is larger
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
Instruction getGreaterOperand() {
none()
}
/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
* of this relational instruction, that is, the side that is smaller
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
Instruction getLesserOperand() {
none()
}
/**
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
*/
predicate isStrict() {
none()
}
}
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() {
opcode instanceof Opcode::CompareLT
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGTInstruction extends CompareInstruction {
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() {
opcode instanceof Opcode::CompareGT
}
}
class CompareLEInstruction extends CompareInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGEInstruction extends CompareInstruction {
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
none()
}
}
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() {
opcode instanceof Opcode::CompareGE
}
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
none()
}
}
class SwitchInstruction extends Instruction {

View File

@@ -197,8 +197,12 @@ cached private module Cached {
)
}
cached Expr getInstructionResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getResultExpression()
cached Expr getInstructionConvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getConvertedResultExpression()
}
cached Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getUnconvertedResultExpression()
}
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {

View File

@@ -89,4 +89,12 @@ class IRBlock extends TIRBlock {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
}
/**
* Holds if this block is reachable from the entry point of its function
*/
final predicate isReachableFromFunctionEntry() {
this = getFunctionIR().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -133,6 +133,16 @@ class Instruction extends Construction::TInstruction {
}
final string toString() {
result = getOpcode().toString() + ": " + getAST().toString()
}
/**
* Gets a string showing the result, opcode, and operands of the instruction, equivalent to what
* would be printed by PrintIR.ql. For example:
*
* `mu0_28(int) = Store r0_26, r0_27`
*/
final string getDumpString() {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
@@ -310,11 +320,19 @@ class Instruction extends Construction::TInstruction {
}
/**
* Gets the `Expr` whose results is computed by this instruction, if any.
* Gets the `Expr` whose result is computed by this instruction, if any.
*/
final Expr getResultExpression() {
result = Construction::getInstructionResultExpression(this)
final Expr getConvertedResultExpression() {
result = Construction::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted `Expr` whose result is computed by this instruction, if any.
*/
final Expr getUnconvertedResultExpression() {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
/**
* Gets the type of the result produced by this instruction. If the
@@ -967,28 +985,110 @@ class CompareNEInstruction extends CompareInstruction {
}
}
class CompareLTInstruction extends CompareInstruction {
/**
* Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() {
opcode instanceof RelationalOpcode
}
/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational instruction, that is, the side that is larger
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
Instruction getGreaterOperand() {
none()
}
/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
* of this relational instruction, that is, the side that is smaller
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
Instruction getLesserOperand() {
none()
}
/**
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
*/
predicate isStrict() {
none()
}
}
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() {
opcode instanceof Opcode::CompareLT
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGTInstruction extends CompareInstruction {
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() {
opcode instanceof Opcode::CompareGT
}
}
class CompareLEInstruction extends CompareInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGEInstruction extends CompareInstruction {
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
none()
}
}
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() {
opcode instanceof Opcode::CompareGE
}
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
none()
}
}
class SwitchInstruction extends Instruction {

View File

@@ -74,13 +74,25 @@ cached private module Cached {
none()
}
cached Expr getInstructionResultExpression(Instruction instruction) {
cached Expr getInstructionConvertedResultExpression(Instruction instruction) {
exists(TranslatedExpr translatedExpr |
translatedExpr = getTranslatedExpr(result) and
instruction = translatedExpr.getResult()
)
}
cached Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
exists(Expr converted, TranslatedExpr translatedExpr |
result = converted.(Conversion).getExpr+()
or
result = converted
|
not result instanceof Conversion and
translatedExpr = getTranslatedExpr(converted) and
instruction = translatedExpr.getResult()
)
}
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
instruction.getTag(), tag)

View File

@@ -89,4 +89,12 @@ class IRBlock extends TIRBlock {
dominates(result.getAPredecessor()) and
not strictlyDominates(result)
}
/**
* Holds if this block is reachable from the entry point of its function
*/
final predicate isReachableFromFunctionEntry() {
this = getFunctionIR().getEntryBlock() or
getAPredecessor().isReachableFromFunctionEntry()
}
}

View File

@@ -133,6 +133,16 @@ class Instruction extends Construction::TInstruction {
}
final string toString() {
result = getOpcode().toString() + ": " + getAST().toString()
}
/**
* Gets a string showing the result, opcode, and operands of the instruction, equivalent to what
* would be printed by PrintIR.ql. For example:
*
* `mu0_28(int) = Store r0_26, r0_27`
*/
final string getDumpString() {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
@@ -310,11 +320,19 @@ class Instruction extends Construction::TInstruction {
}
/**
* Gets the `Expr` whose results is computed by this instruction, if any.
* Gets the `Expr` whose result is computed by this instruction, if any.
*/
final Expr getResultExpression() {
result = Construction::getInstructionResultExpression(this)
final Expr getConvertedResultExpression() {
result = Construction::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted `Expr` whose result is computed by this instruction, if any.
*/
final Expr getUnconvertedResultExpression() {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
/**
* Gets the type of the result produced by this instruction. If the
@@ -967,28 +985,110 @@ class CompareNEInstruction extends CompareInstruction {
}
}
class CompareLTInstruction extends CompareInstruction {
/**
* Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() {
opcode instanceof RelationalOpcode
}
/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational instruction, that is, the side that is larger
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
Instruction getGreaterOperand() {
none()
}
/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
* of this relational instruction, that is, the side that is smaller
* if the overall instruction evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
Instruction getLesserOperand() {
none()
}
/**
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
*/
predicate isStrict() {
none()
}
}
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() {
opcode instanceof Opcode::CompareLT
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGTInstruction extends CompareInstruction {
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() {
opcode instanceof Opcode::CompareGT
}
}
class CompareLEInstruction extends CompareInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
any()
}
}
class CompareGEInstruction extends CompareInstruction {
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() {
opcode instanceof Opcode::CompareLE
}
override Instruction getLesserOperand() {
result = getLeftOperand()
}
override Instruction getGreaterOperand() {
result = getRightOperand()
}
override predicate isStrict() {
none()
}
}
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() {
opcode instanceof Opcode::CompareGE
}
override Instruction getLesserOperand() {
result = getRightOperand()
}
override Instruction getGreaterOperand() {
result = getLeftOperand()
}
override predicate isStrict() {
none()
}
}
class SwitchInstruction extends Instruction {

View File

@@ -197,8 +197,12 @@ cached private module Cached {
)
}
cached Expr getInstructionResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getResultExpression()
cached Expr getInstructionConvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getConvertedResultExpression()
}
cached Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getUnconvertedResultExpression()
}
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {

View File

@@ -12,7 +12,8 @@ class Printf extends FormattingFunction {
hasGlobalName("wprintf") or
hasGlobalName("wprintf_s") or
hasGlobalName("g_printf")
)
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() { result=0 }
@@ -26,7 +27,15 @@ class Printf extends FormattingFunction {
* The standard functions `fprintf`, `fwprintf` and their glib variants.
*/
class Fprintf extends FormattingFunction {
Fprintf() { this instanceof TopLevelFunction and (hasGlobalName("fprintf") or hasGlobalName("fwprintf") or hasGlobalName("g_fprintf"))}
Fprintf() {
this instanceof TopLevelFunction and
(
hasGlobalName("fprintf") or
hasGlobalName("fwprintf") or
hasGlobalName("g_fprintf")
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() { result=1 }
override predicate isWideCharDefault() { hasGlobalName("fwprintf") }
@@ -47,7 +56,8 @@ class Sprintf extends FormattingFunction {
hasGlobalName("g_strdup_printf") or
hasGlobalName("g_sprintf") or
hasGlobalName("__builtin___sprintf_chk")
)
) and
not exists(getDefinition().getFile().getRelativePath())
}
override predicate isWideCharDefault() {
@@ -100,7 +110,8 @@ class Snprintf extends FormattingFunction {
or hasGlobalName("g_snprintf")
or hasGlobalName("wnsprintf")
or hasGlobalName("__builtin___snprintf_chk")
)
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() {
@@ -133,10 +144,13 @@ class Snprintf extends FormattingFunction {
* in the buffer.
*/
predicate returnsFullFormatLength() {
hasGlobalName("snprintf") or
hasGlobalName("g_snprintf") or
hasGlobalName("__builtin___snprintf_chk") or
hasGlobalName("snprintf_s")
(
hasGlobalName("snprintf") or
hasGlobalName("g_snprintf") or
hasGlobalName("__builtin___snprintf_chk") or
hasGlobalName("snprintf_s")
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getSizeParameterIndex() {
@@ -158,7 +172,8 @@ class StringCchPrintf extends FormattingFunction {
or hasGlobalName("StringCbPrintfEx")
or hasGlobalName("StringCbPrintf_l")
or hasGlobalName("StringCbPrintf_lEx")
)
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() {
@@ -187,7 +202,8 @@ class Syslog extends FormattingFunction {
Syslog() {
this instanceof TopLevelFunction and (
hasGlobalName("syslog")
)
) and
not exists(getDefinition().getFile().getRelativePath())
}
override int getFormatParameterIndex() { result=1 }

View File

@@ -7,6 +7,38 @@
*/
import semmle.code.cpp.Function
private Type stripTopLevelSpecifiersOnly(Type t) {
(
result = stripTopLevelSpecifiersOnly(t.(SpecifiedType).getBaseType())
) or (
result = t and
not t instanceof SpecifiedType
)
}
/**
* A type that is used as a format string by any formatting function.
*/
Type getAFormatterWideType() {
exists(FormattingFunction ff |
result = stripTopLevelSpecifiersOnly(ff.getDefaultCharType()) and
result.getSize() != 1
)
}
/**
* A type that is used as a format string by any formatting function, or `wchar_t` if
* there is none.
*/
private Type getAFormatterWideTypeOrDefault() {
result = getAFormatterWideType() or
(
not exists(getAFormatterWideType()) and
result instanceof Wchar_t
)
}
/**
* A standard library function that uses a `printf`-like formatting string.
*/
@@ -20,6 +52,43 @@ abstract class FormattingFunction extends Function {
*/
predicate isWideCharDefault() { none() }
/**
* Gets the default character type expected for `%s` by this function. Typically
* `char` or `wchar_t`.
*/
Type getDefaultCharType() {
result = stripTopLevelSpecifiersOnly(getParameter(getFormatParameterIndex()).getType().
getUnderlyingType().(PointerType).getBaseType())
}
/**
* Gets the non-default character type expected for `%S` by this function. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
Type getNonDefaultCharType() {
(
getDefaultCharType().getSize() = 1 and
result = getAFormatterWideTypeOrDefault()
) or (
getDefaultCharType().getSize() > 1 and
result instanceof PlainCharType
)
}
/**
* Gets the wide character type for this function. This is usually `wchar_t`. On some
* snapshots there may be multiple results where we can't tell which is correct for a
* particular function.
*/
Type getWideCharType() {
(
result = getDefaultCharType() or
result = getNonDefaultCharType()
) and
result.getSize() > 1
}
/**
* Gets the position at which the output parameter, if any, occurs.
*/

View File

@@ -633,12 +633,26 @@ class PointsToExpr extends Expr
pragma[noopt]
Element pointsTo()
{
this.interesting() and exists(int set, @element thisEntity, @element resultEntity | thisEntity = underlyingElement(this) and pointstosets(set, thisEntity) and setlocations(set, resultEntity) and resultEntity = unresolveElement(result))
this.interesting() and
exists(int set, @element thisEntity, @element resultEntity |
thisEntity = underlyingElement(this) and
pointstosets(set, thisEntity) and
setlocations(set, resultEntity) and
resultEntity = localUnresolveElement(result)
)
}
float confidence() { result = 1.0 / count(this.pointsTo()) }
}
/*
* This is used above in a `pragma[noopt]` context, which prevents its
* customary inlining. We materialise it explicitly here.
*/
private @element localUnresolveElement(Element e) {
result = unresolveElement(e)
}
/**
* Holds if anything points to an element, that is, is equivalent to:
* ```

View File

@@ -44,6 +44,7 @@
import cpp
private import RangeAnalysisUtils
import RangeSSA
import SimpleRangeAnalysisCached
/**
* This fixed set of lower bounds is used when the lower bounds of an
@@ -406,27 +407,6 @@ deprecated predicate negative_overflow(Expr expr) {
exprMightOverflowNegatively(expr)
}
/**
* Holds if the expression might overflow negatively. This predicate
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
cached
predicate exprMightOverflowNegatively(Expr expr) {
getLowerBoundsImpl(expr) < exprMinVal(expr)
}
/**
* Holds if the expression might overflow negatively. Conversions
* are also taken into account. For example the expression
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
* due to the addition.
*/
cached
predicate convertedExprMightOverflowNegatively(Expr expr) {
exprMightOverflowNegatively(expr) or
convertedExprMightOverflowNegatively(expr.getConversion())
}
/**
* Holds if the expression might overflow positively. This predicate
@@ -439,39 +419,6 @@ deprecated predicate positive_overflow(Expr expr) {
exprMightOverflowPositively(expr)
}
/**
* Holds if the expression might overflow positively. This predicate
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
cached
predicate exprMightOverflowPositively(Expr expr) {
getUpperBoundsImpl(expr) > exprMaxVal(expr)
}
/**
* Holds if the expression might overflow positively. Conversions
* are also taken into account. For example the expression
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
* due to the addition.
*/
cached
predicate convertedExprMightOverflowPositively(Expr expr) {
exprMightOverflowPositively(expr) or
convertedExprMightOverflowPositively(expr.getConversion())
}
/**
* Holds if the expression might overflow (either positively or
* negatively). The possibility that the expression might overflow
* due to an implicit or explicit cast is also considered.
*/
cached
predicate convertedExprMightOverflow(Expr expr) {
convertedExprMightOverflowNegatively(expr) or
convertedExprMightOverflowPositively(expr)
}
/** Only to be called by `getTruncatedLowerBounds`. */
private
float getLowerBoundsImpl(Expr expr) {
@@ -921,28 +868,6 @@ float getDefUpperBoundsImpl(RangeSsaDefinition def, LocalScopeVariable v) {
unanalyzableDefBounds(def, v, _, result)
}
/** Holds if the definition might overflow negatively. */
cached
predicate defMightOverflowNegatively(RangeSsaDefinition def, LocalScopeVariable v) {
getDefLowerBoundsImpl(def, v) < varMinVal(v)
}
/** Holds if the definition might overflow positively. */
cached
predicate defMightOverflowPositively(RangeSsaDefinition def, LocalScopeVariable v) {
getDefUpperBoundsImpl(def, v) > varMaxVal(v)
}
/**
* Holds if the definition might overflow (either positively or
* negatively).
*/
cached
predicate defMightOverflow(RangeSsaDefinition def, LocalScopeVariable v) {
defMightOverflowNegatively(def, v) or
defMightOverflowPositively(def, v)
}
/**
* Get the lower bounds for a `RangeSsaDefinition`. Most of the work is
* done by `getDefLowerBoundsImpl`, but this is where widening is applied
@@ -1133,63 +1058,142 @@ predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBound) {
(isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted()))
}
/**
* Gets the lower bound of the expression.
*
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
* different result type. Such casts can cause the value of the expression
* to overflow or to be truncated. This predicate computes the lower bound
* of the expression without including the effect of the casts. To compute
* the lower bound of the expression after all the casts have been applied,
* call `lowerBound` like this:
*
* `lowerBound(expr.getFullyConverted())`
*/
cached
float lowerBound(Expr expr) {
// Combine the lower bounds returned by getTruncatedLowerBounds into a
// single minimum value.
result = min(float lb | lb = getTruncatedLowerBounds(expr) | lb)
}
private cached module SimpleRangeAnalysisCached {
/**
* Gets the lower bound of the expression.
*
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
* different result type. Such casts can cause the value of the expression
* to overflow or to be truncated. This predicate computes the lower bound
* of the expression without including the effect of the casts. To compute
* the lower bound of the expression after all the casts have been applied,
* call `lowerBound` like this:
*
* `lowerBound(expr.getFullyConverted())`
*/
cached
float lowerBound(Expr expr) {
// Combine the lower bounds returned by getTruncatedLowerBounds into a
// single minimum value.
result = min(float lb | lb = getTruncatedLowerBounds(expr) | lb)
}
/**
* Gets the upper bound of the expression.
*
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
* different result type. Such casts can cause the value of the expression
* to overflow or to be truncated. This predicate computes the upper bound
* of the expression without including the effect of the casts. To compute
* the upper bound of the expression after all the casts have been applied,
* call `upperBound` like this:
*
* `upperBound(expr.getFullyConverted())`
*/
cached
float upperBound(Expr expr) {
// Combine the upper bounds returned by getTruncatedUpperBounds into a
// single maximum value.
result = max(float ub | ub = getTruncatedUpperBounds(expr) | ub)
}
/**
* Holds if `expr` has a provably empty range. For example:
*
* 10 < expr and expr < 5
*
* The range of an expression can only be empty if it can never be
* executed. For example:
*
* if (10 < x) {
* if (x < 5) {
* // Unreachable code
* return x; // x has an empty range: 10 < x && x < 5
* }
* }
*/
cached
predicate exprWithEmptyRange(Expr expr) {
analyzableExpr(expr) and
(not exists(lowerBound(expr)) or
not exists(upperBound(expr)) or
lowerBound(expr) > upperBound(expr))
}
/**
* Gets the upper bound of the expression.
*
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
* different result type. Such casts can cause the value of the expression
* to overflow or to be truncated. This predicate computes the upper bound
* of the expression without including the effect of the casts. To compute
* the upper bound of the expression after all the casts have been applied,
* call `upperBound` like this:
*
* `upperBound(expr.getFullyConverted())`
*/
cached
float upperBound(Expr expr) {
// Combine the upper bounds returned by getTruncatedUpperBounds into a
// single maximum value.
result = max(float ub | ub = getTruncatedUpperBounds(expr) | ub)
}
/**
* Holds if `expr` has a provably empty range. For example:
*
* 10 < expr and expr < 5
*
* The range of an expression can only be empty if it can never be
* executed. For example:
*
* if (10 < x) {
* if (x < 5) {
* // Unreachable code
* return x; // x has an empty range: 10 < x && x < 5
* }
* }
*/
cached
predicate exprWithEmptyRange(Expr expr) {
analyzableExpr(expr) and
(not exists(lowerBound(expr)) or
not exists(upperBound(expr)) or
lowerBound(expr) > upperBound(expr))
/** Holds if the definition might overflow negatively. */
cached
predicate defMightOverflowNegatively(RangeSsaDefinition def, LocalScopeVariable v) {
getDefLowerBoundsImpl(def, v) < varMinVal(v)
}
/** Holds if the definition might overflow positively. */
cached
predicate defMightOverflowPositively(RangeSsaDefinition def, LocalScopeVariable v) {
getDefUpperBoundsImpl(def, v) > varMaxVal(v)
}
/**
* Holds if the definition might overflow (either positively or
* negatively).
*/
cached
predicate defMightOverflow(RangeSsaDefinition def, LocalScopeVariable v) {
defMightOverflowNegatively(def, v) or
defMightOverflowPositively(def, v)
}
/**
* Holds if the expression might overflow negatively. This predicate
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
cached
predicate exprMightOverflowNegatively(Expr expr) {
getLowerBoundsImpl(expr) < exprMinVal(expr)
}
/**
* Holds if the expression might overflow negatively. Conversions
* are also taken into account. For example the expression
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
* due to the addition.
*/
cached
predicate convertedExprMightOverflowNegatively(Expr expr) {
exprMightOverflowNegatively(expr) or
convertedExprMightOverflowNegatively(expr.getConversion())
}
/**
* Holds if the expression might overflow positively. This predicate
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
cached
predicate exprMightOverflowPositively(Expr expr) {
getUpperBoundsImpl(expr) > exprMaxVal(expr)
}
/**
* Holds if the expression might overflow positively. Conversions
* are also taken into account. For example the expression
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
* due to the addition.
*/
cached
predicate convertedExprMightOverflowPositively(Expr expr) {
exprMightOverflowPositively(expr) or
convertedExprMightOverflowPositively(expr.getConversion())
}
/**
* Holds if the expression might overflow (either positively or
* negatively). The possibility that the expression might overflow
* due to an implicit or explicit cast is also considered.
*/
cached
predicate convertedExprMightOverflow(Expr expr) {
convertedExprMightOverflowNegatively(expr) or
convertedExprMightOverflowPositively(expr)
}
}

View File

@@ -13,6 +13,7 @@ predicate guardedAbs(Operation e, Expr use) {
}
/** is the size of this use guarded to be less than something? */
pragma[nomagic]
predicate guardedLesser(Operation e, Expr use) {
exists(IfStmt c, RelationalOperation guard |
use = guard.getLesserOperand().getAChild*() and
@@ -33,6 +34,7 @@ predicate guardedLesser(Operation e, Expr use) {
}
/** is the size of this use guarded to be greater than something? */
pragma[nomagic]
predicate guardedGreater(Operation e, Expr use) {
exists(IfStmt c, RelationalOperation guard |
use = guard.getGreaterOperand().getAChild*() and

View File

@@ -312,7 +312,7 @@ private predicate analyzableLocalScopeVariable(VariableAccess access) {
strictcount (SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count (SsaDefinition def, Variable v
| def.getAUse(v) = access
| def.getDefiningValue(v)) <= 1 and
| def.getDefiningValue(v).getFullyConverted()) <= 1 and
not analyzableConst(access)
}

View File

@@ -0,0 +1,154 @@
int test(int x, int w, int z) {
int j;
long y = 50;
// simple comparison
if (x > 0) {
y = 20;
z = 10;
} else {
y = 30;
}
z = x + y;
// More complex
if(x < 0 && y > 1)
y = 40;
else
y = 20; /* The && expression does not control this block as the x<0 expression jumps here if false. */
z = 10;
// while loop
while(x > 0) {
y = 10;
x--;
}
z += y;
// for loop
for(j = 0; j < 10; j++) {
y = 0;
w = 10;
}
z += w;
// nested control flow
for(j = 0; j < 10; j++) {
y = 30;
if(z > 0)
if(y > 0) {
w = 0;
break;
} else {
w = 20;
}
else {
w = 10;
continue;
}
x = 0;
}
if (x == 0 || y < 0) {
y = 60;
z = 10;
} else
return z;
z += x;
return 0;
}
int test2(int x, int w, int z) {
int j;
long y = 50;
// simple comparison
if (x == 0) {
y = 20;
z = 10;
} else {
y = 30;
}
z = x + y;
// More complex
if(x == 0 && y != 0)
y = 40;
else
y = 20;
z = 10;
// while loop
while(x != 0) {
y = 10;
x--;
}
z += y;
// for loop
for(j = 0; j < 10; j++) {
y = 0;
w = 10;
}
z += w;
if (x == 0 || y < 0) {
y = 60;
z = 10;
} else
return z;
z += x;
return 0;
}
int test3_condition();
void test3_action();
void test3() {
int b = 0;
if (1 && test3_condition()) {
b = 1;
test3_action();
}
if (b) {
test3_action();
}
}
void test4(int i) {
if (0) {
if (i) {
;
}
}
return;
}
void test5(int x) {
if (!x) {
test3();
}
}
void test6(int x, int y) {
return x && y;
}

View File

@@ -0,0 +1,54 @@
class X
{
public:
void set();
};
class Y
{
public:
Y* f();
const X* get() const { return 0; }
X* get() { return 0; }
};
Y* Y::f()
{
if ( get() )
get()->set();
return 0;
}
class Error {
public:
Error() {}
};
bool getABool();
void doSomething(int x) {
if (x == -1) {
throw new Error();
}
}
void doSomethingElse(int x);
bool testWithCatch0(int v)
{
try
{
if( getABool() )
{
doSomething(v);
return true;
}
}
catch(Error &e)
{
doSomethingElse(v);
}
return false;
}

View File

@@ -0,0 +1,730 @@
astGuards
| test.c:7:9:7:13 | ... > ... |
| test.c:17:8:17:12 | ... < ... |
| test.c:17:8:17:21 | ... && ... |
| test.c:17:17:17:21 | ... > ... |
| test.c:26:11:26:15 | ... > ... |
| test.c:34:16:34:21 | ... < ... |
| test.c:42:16:42:21 | ... < ... |
| test.c:44:12:44:16 | ... > ... |
| test.c:45:16:45:20 | ... > ... |
| test.c:58:9:58:14 | ... == ... |
| test.c:58:9:58:23 | ... \|\| ... |
| test.c:58:19:58:23 | ... < ... |
| test.c:75:9:75:14 | ... == ... |
| test.c:85:8:85:13 | ... == ... |
| test.c:85:8:85:23 | ... && ... |
| test.c:85:18:85:23 | ... != ... |
| test.c:94:11:94:16 | ... != ... |
| test.c:102:16:102:21 | ... < ... |
| test.c:109:9:109:14 | ... == ... |
| test.c:109:9:109:23 | ... \|\| ... |
| test.c:109:19:109:23 | ... < ... |
| test.c:126:7:126:7 | 1 |
| test.c:126:7:126:28 | ... && ... |
| test.c:126:12:126:26 | call to test3_condition |
| test.c:131:7:131:7 | b |
| test.c:137:7:137:7 | 0 |
| test.c:138:9:138:9 | i |
| test.c:146:7:146:8 | ! ... |
| test.c:146:8:146:8 | x |
| test.c:152:10:152:10 | x |
| test.c:152:10:152:15 | ... && ... |
| test.c:152:15:152:15 | y |
| test.cpp:18:8:18:10 | call to get |
| test.cpp:31:7:31:13 | ... == ... |
| test.cpp:42:13:42:20 | call to getABool |
astGuardsCompare
| 7 | 0 < x+0 when ... > ... is true |
| 7 | 0 >= x+0 when ... > ... is false |
| 7 | x < 0+1 when ... > ... is false |
| 7 | x >= 0+1 when ... > ... is true |
| 17 | 0 < x+1 when ... < ... is false |
| 17 | 0 >= x+1 when ... && ... is true |
| 17 | 0 >= x+1 when ... < ... is true |
| 17 | 1 < y+0 when ... && ... is true |
| 17 | 1 < y+0 when ... > ... is true |
| 17 | 1 >= y+0 when ... > ... is false |
| 17 | x < 0+0 when ... && ... is true |
| 17 | x < 0+0 when ... < ... is true |
| 17 | x >= 0+0 when ... < ... is false |
| 17 | y < 1+1 when ... > ... is false |
| 17 | y >= 1+1 when ... && ... is true |
| 17 | y >= 1+1 when ... > ... is true |
| 26 | 0 < x+0 when ... > ... is true |
| 26 | 0 >= x+0 when ... > ... is false |
| 26 | x < 0+1 when ... > ... is false |
| 26 | x >= 0+1 when ... > ... is true |
| 31 | - ... != x+0 when ... == ... is false |
| 31 | - ... == x+0 when ... == ... is true |
| 31 | x != - ...+0 when ... == ... is false |
| 31 | x == - ...+0 when ... == ... is true |
| 34 | 10 < j+1 when ... < ... is false |
| 34 | 10 >= j+1 when ... < ... is true |
| 34 | j < 10+0 when ... < ... is true |
| 34 | j >= 10+0 when ... < ... is false |
| 42 | 10 < j+1 when ... < ... is false |
| 42 | 10 >= j+1 when ... < ... is true |
| 42 | j < 10+0 when ... < ... is true |
| 42 | j >= 10+0 when ... < ... is false |
| 44 | 0 < z+0 when ... > ... is true |
| 44 | 0 >= z+0 when ... > ... is false |
| 44 | z < 0+1 when ... > ... is false |
| 44 | z >= 0+1 when ... > ... is true |
| 45 | 0 < y+0 when ... > ... is true |
| 45 | 0 >= y+0 when ... > ... is false |
| 45 | y < 0+1 when ... > ... is false |
| 45 | y >= 0+1 when ... > ... is true |
| 58 | 0 != x+0 when ... == ... is false |
| 58 | 0 != x+0 when ... \|\| ... is false |
| 58 | 0 < y+1 when ... < ... is false |
| 58 | 0 < y+1 when ... \|\| ... is false |
| 58 | 0 == x+0 when ... == ... is true |
| 58 | 0 >= y+1 when ... < ... is true |
| 58 | x != 0+0 when ... == ... is false |
| 58 | x != 0+0 when ... \|\| ... is false |
| 58 | x == 0+0 when ... == ... is true |
| 58 | y < 0+0 when ... < ... is true |
| 58 | y >= 0+0 when ... < ... is false |
| 58 | y >= 0+0 when ... \|\| ... is false |
| 75 | 0 != x+0 when ... == ... is false |
| 75 | 0 == x+0 when ... == ... is true |
| 75 | x != 0+0 when ... == ... is false |
| 75 | x == 0+0 when ... == ... is true |
| 85 | 0 != x+0 when ... == ... is false |
| 85 | 0 != y+0 when ... != ... is true |
| 85 | 0 != y+0 when ... && ... is true |
| 85 | 0 == x+0 when ... && ... is true |
| 85 | 0 == x+0 when ... == ... is true |
| 85 | 0 == y+0 when ... != ... is false |
| 85 | x != 0+0 when ... == ... is false |
| 85 | x == 0+0 when ... && ... is true |
| 85 | x == 0+0 when ... == ... is true |
| 85 | y != 0+0 when ... != ... is true |
| 85 | y != 0+0 when ... && ... is true |
| 85 | y == 0+0 when ... != ... is false |
| 94 | 0 != x+0 when ... != ... is true |
| 94 | 0 == x+0 when ... != ... is false |
| 94 | x != 0+0 when ... != ... is true |
| 94 | x == 0+0 when ... != ... is false |
| 102 | 10 < j+1 when ... < ... is false |
| 102 | 10 >= j+1 when ... < ... is true |
| 102 | j < 10+0 when ... < ... is true |
| 102 | j >= 10+0 when ... < ... is false |
| 109 | 0 != x+0 when ... == ... is false |
| 109 | 0 != x+0 when ... \|\| ... is false |
| 109 | 0 < y+1 when ... < ... is false |
| 109 | 0 < y+1 when ... \|\| ... is false |
| 109 | 0 == x+0 when ... == ... is true |
| 109 | 0 >= y+1 when ... < ... is true |
| 109 | x != 0+0 when ... == ... is false |
| 109 | x != 0+0 when ... \|\| ... is false |
| 109 | x == 0+0 when ... == ... is true |
| 109 | y < 0+0 when ... < ... is true |
| 109 | y >= 0+0 when ... < ... is false |
| 109 | y >= 0+0 when ... \|\| ... is false |
astGuardsControl
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
| test.c:7:9:7:13 | ... > ... | true | 7 | 9 |
| test.c:17:8:17:12 | ... < ... | true | 17 | 17 |
| test.c:17:8:17:12 | ... < ... | true | 18 | 18 |
| test.c:17:8:17:21 | ... && ... | true | 18 | 18 |
| test.c:17:17:17:21 | ... > ... | true | 18 | 18 |
| test.c:26:11:26:15 | ... > ... | false | 2 | 2 |
| test.c:26:11:26:15 | ... > ... | false | 31 | 34 |
| test.c:26:11:26:15 | ... > ... | false | 34 | 34 |
| test.c:26:11:26:15 | ... > ... | false | 39 | 42 |
| test.c:26:11:26:15 | ... > ... | false | 42 | 42 |
| test.c:26:11:26:15 | ... > ... | false | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | false | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | false | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | false | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | false | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | false | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | false | 58 | 58 |
| test.c:26:11:26:15 | ... > ... | false | 58 | 66 |
| test.c:26:11:26:15 | ... > ... | false | 62 | 62 |
| test.c:26:11:26:15 | ... > ... | true | 26 | 28 |
| test.c:34:16:34:21 | ... < ... | false | 2 | 2 |
| test.c:34:16:34:21 | ... < ... | false | 39 | 42 |
| test.c:34:16:34:21 | ... < ... | false | 42 | 42 |
| test.c:34:16:34:21 | ... < ... | false | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | false | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | false | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | false | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | false | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | false | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | false | 58 | 58 |
| test.c:34:16:34:21 | ... < ... | false | 58 | 66 |
| test.c:34:16:34:21 | ... < ... | false | 62 | 62 |
| test.c:34:16:34:21 | ... < ... | true | 34 | 34 |
| test.c:42:16:42:21 | ... < ... | true | 42 | 42 |
| test.c:42:16:42:21 | ... < ... | true | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | true | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | true | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | true | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | true | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | false | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | true | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | true | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | true | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | false | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | true | 45 | 47 |
| test.c:58:9:58:14 | ... == ... | false | 58 | 58 |
| test.c:58:9:58:14 | ... == ... | false | 62 | 62 |
| test.c:58:9:58:23 | ... \|\| ... | false | 62 | 62 |
| test.c:58:19:58:23 | ... < ... | false | 62 | 62 |
| test.c:75:9:75:14 | ... == ... | false | 78 | 79 |
| test.c:75:9:75:14 | ... == ... | true | 75 | 77 |
| test.c:85:8:85:13 | ... == ... | true | 85 | 85 |
| test.c:85:8:85:13 | ... == ... | true | 86 | 86 |
| test.c:85:8:85:23 | ... && ... | true | 86 | 86 |
| test.c:85:18:85:23 | ... != ... | true | 86 | 86 |
| test.c:94:11:94:16 | ... != ... | false | 70 | 70 |
| test.c:94:11:94:16 | ... != ... | false | 99 | 102 |
| test.c:94:11:94:16 | ... != ... | false | 102 | 102 |
| test.c:94:11:94:16 | ... != ... | false | 107 | 109 |
| test.c:94:11:94:16 | ... != ... | false | 109 | 109 |
| test.c:94:11:94:16 | ... != ... | false | 109 | 117 |
| test.c:94:11:94:16 | ... != ... | false | 113 | 113 |
| test.c:94:11:94:16 | ... != ... | true | 94 | 96 |
| test.c:102:16:102:21 | ... < ... | false | 70 | 70 |
| test.c:102:16:102:21 | ... < ... | false | 107 | 109 |
| test.c:102:16:102:21 | ... < ... | false | 109 | 109 |
| test.c:102:16:102:21 | ... < ... | false | 109 | 117 |
| test.c:102:16:102:21 | ... < ... | false | 113 | 113 |
| test.c:102:16:102:21 | ... < ... | true | 102 | 102 |
| test.c:109:9:109:14 | ... == ... | false | 109 | 109 |
| test.c:109:9:109:14 | ... == ... | false | 113 | 113 |
| test.c:109:9:109:23 | ... \|\| ... | false | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | false | 113 | 113 |
| test.c:126:7:126:7 | 1 | true | 126 | 126 |
| test.c:126:7:126:7 | 1 | true | 126 | 128 |
| test.c:126:7:126:28 | ... && ... | true | 126 | 128 |
| test.c:126:12:126:26 | call to test3_condition | true | 126 | 128 |
| test.c:131:7:131:7 | b | true | 131 | 132 |
| test.c:137:7:137:7 | 0 | true | 137 | 138 |
| test.c:137:7:137:7 | 0 | true | 138 | 139 |
| test.c:138:9:138:9 | i | true | 138 | 139 |
| test.c:146:7:146:8 | ! ... | true | 146 | 147 |
| test.c:146:8:146:8 | x | false | 146 | 147 |
| test.c:152:10:152:10 | x | true | 151 | 152 |
| test.c:152:10:152:10 | x | true | 152 | 152 |
| test.c:152:10:152:15 | ... && ... | true | 151 | 152 |
| test.c:152:15:152:15 | y | true | 151 | 152 |
| test.cpp:18:8:18:10 | call to get | true | 19 | 19 |
| test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | true | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |
astGuardsEnsure
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | < | test.c:7:13:7:13 | 0 | 1 | 10 | 11 |
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | >= | test.c:7:13:7:13 | 0 | 1 | 7 | 9 |
| test.c:7:9:7:13 | ... > ... | test.c:7:13:7:13 | 0 | < | test.c:7:9:7:9 | x | 0 | 7 | 9 |
| test.c:7:9:7:13 | ... > ... | test.c:7:13:7:13 | 0 | >= | test.c:7:9:7:9 | x | 0 | 10 | 11 |
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 17 | 17 |
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 18 | 18 |
| test.c:17:8:17:12 | ... < ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 17 | 17 |
| test.c:17:8:17:12 | ... < ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 18 | 18 |
| test.c:17:8:17:21 | ... && ... | test.c:17:8:17:8 | x | < | test.c:17:12:17:12 | 0 | 0 | 18 | 18 |
| test.c:17:8:17:21 | ... && ... | test.c:17:12:17:12 | 0 | >= | test.c:17:8:17:8 | x | 1 | 18 | 18 |
| test.c:17:8:17:21 | ... && ... | test.c:17:17:17:17 | y | >= | test.c:17:21:17:21 | 1 | 1 | 18 | 18 |
| test.c:17:8:17:21 | ... && ... | test.c:17:21:17:21 | 1 | < | test.c:17:17:17:17 | y | 0 | 18 | 18 |
| test.c:17:17:17:21 | ... > ... | test.c:17:17:17:17 | y | >= | test.c:17:21:17:21 | 1 | 1 | 18 | 18 |
| test.c:17:17:17:21 | ... > ... | test.c:17:21:17:21 | 1 | < | test.c:17:17:17:17 | y | 0 | 18 | 18 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 2 | 2 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 31 | 34 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 34 | 34 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 39 | 42 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 42 | 42 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 58 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 58 | 66 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | test.c:26:15:26:15 | 0 | 1 | 62 | 62 |
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | >= | test.c:26:15:26:15 | 0 | 1 | 26 | 28 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | < | test.c:26:11:26:11 | x | 0 | 26 | 28 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 2 | 2 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 31 | 34 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 34 | 34 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 39 | 42 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 42 | 42 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 42 | 44 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 45 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 45 | 47 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 48 | 55 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 51 | 53 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 56 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 58 | 58 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 58 | 66 |
| test.c:26:11:26:15 | ... > ... | test.c:26:15:26:15 | 0 | >= | test.c:26:11:26:11 | x | 0 | 62 | 62 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | < | test.c:34:20:34:21 | 10 | 0 | 34 | 34 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 2 | 2 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 39 | 42 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 42 | 42 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 58 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 58 | 66 |
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | test.c:34:20:34:21 | 10 | 0 | 62 | 62 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 2 | 2 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 39 | 42 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 42 | 42 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 42 | 44 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 45 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 45 | 47 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 48 | 55 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 51 | 53 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 56 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 58 | 58 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 58 | 66 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | < | test.c:34:16:34:16 | j | 1 | 62 | 62 |
| test.c:34:16:34:21 | ... < ... | test.c:34:20:34:21 | 10 | >= | test.c:34:16:34:16 | j | 1 | 34 | 34 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 42 | 42 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | test.c:42:20:42:21 | 10 | 0 | 51 | 53 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 42 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 42 | 44 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 45 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 45 | 47 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 48 | 55 |
| test.c:42:16:42:21 | ... < ... | test.c:42:20:42:21 | 10 | >= | test.c:42:16:42:16 | j | 1 | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | test.c:44:16:44:16 | 0 | 1 | 51 | 53 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | test.c:44:16:44:16 | 0 | 1 | 48 | 55 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 45 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 45 | 47 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | < | test.c:44:12:44:12 | z | 0 | 48 | 55 |
| test.c:44:12:44:16 | ... > ... | test.c:44:16:44:16 | 0 | >= | test.c:44:12:44:12 | z | 0 | 51 | 53 |
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | < | test.c:45:20:45:20 | 0 | 1 | 48 | 55 |
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | >= | test.c:45:20:45:20 | 0 | 1 | 45 | 47 |
| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | < | test.c:45:16:45:16 | y | 0 | 45 | 47 |
| test.c:45:16:45:20 | ... > ... | test.c:45:20:45:20 | 0 | >= | test.c:45:16:45:16 | y | 0 | 48 | 55 |
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 58 | 58 |
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 62 | 62 |
| test.c:58:9:58:14 | ... == ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 58 | 58 |
| test.c:58:9:58:14 | ... == ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 62 | 62 |
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | test.c:58:14:58:14 | 0 | 0 | 62 | 62 |
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:14:58:14 | 0 | != | test.c:58:9:58:9 | x | 0 | 62 | 62 |
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:19:58:19 | y | >= | test.c:58:23:58:23 | 0 | 0 | 62 | 62 |
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:23:58:23 | 0 | < | test.c:58:19:58:19 | y | 1 | 62 | 62 |
| test.c:58:19:58:23 | ... < ... | test.c:58:19:58:19 | y | >= | test.c:58:23:58:23 | 0 | 0 | 62 | 62 |
| test.c:58:19:58:23 | ... < ... | test.c:58:23:58:23 | 0 | < | test.c:58:19:58:19 | y | 1 | 62 | 62 |
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | test.c:75:14:75:14 | 0 | 0 | 78 | 79 |
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 75 | 77 |
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | != | test.c:75:9:75:9 | x | 0 | 78 | 79 |
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 75 | 77 |
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 85 | 85 |
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 85 | 85 |
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
| test.c:85:8:85:23 | ... && ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 |
| test.c:85:8:85:23 | ... && ... | test.c:85:23:85:23 | 0 | != | test.c:85:18:85:18 | y | 0 | 86 | 86 |
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 |
| test.c:85:18:85:23 | ... != ... | test.c:85:23:85:23 | 0 | != | test.c:85:18:85:18 | y | 0 | 86 | 86 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | test.c:94:16:94:16 | 0 | 0 | 94 | 96 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 70 | 70 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 99 | 102 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 102 | 102 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 107 | 109 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 109 | 109 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 109 | 117 |
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | test.c:94:16:94:16 | 0 | 0 | 113 | 113 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | != | test.c:94:11:94:11 | x | 0 | 94 | 96 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 70 | 70 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 99 | 102 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 102 | 102 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 107 | 109 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 109 | 109 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 109 | 117 |
| test.c:94:11:94:16 | ... != ... | test.c:94:16:94:16 | 0 | == | test.c:94:11:94:11 | x | 0 | 113 | 113 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | < | test.c:102:20:102:21 | 10 | 0 | 102 | 102 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 70 | 70 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 107 | 109 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 109 | 109 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 109 | 117 |
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | test.c:102:20:102:21 | 10 | 0 | 113 | 113 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 70 | 70 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 107 | 109 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 109 | 109 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 109 | 117 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | < | test.c:102:16:102:16 | j | 1 | 113 | 113 |
| test.c:102:16:102:21 | ... < ... | test.c:102:20:102:21 | 10 | >= | test.c:102:16:102:16 | j | 1 | 102 | 102 |
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 109 | 109 |
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 113 | 113 |
| test.c:109:9:109:14 | ... == ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 109 | 109 |
| test.c:109:9:109:14 | ... == ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 113 | 113 |
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | test.c:109:14:109:14 | 0 | 0 | 113 | 113 |
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:14:109:14 | 0 | != | test.c:109:9:109:9 | x | 0 | 113 | 113 |
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 |
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 |
| test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 31 | 32 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
irGuards
| test.c:7:9:7:13 | CompareGT: ... > ... |
| test.c:17:8:17:12 | CompareLT: ... < ... |
| test.c:17:17:17:21 | CompareGT: ... > ... |
| test.c:26:11:26:15 | CompareGT: ... > ... |
| test.c:34:16:34:21 | CompareLT: ... < ... |
| test.c:42:16:42:21 | CompareLT: ... < ... |
| test.c:44:12:44:16 | CompareGT: ... > ... |
| test.c:45:16:45:20 | CompareGT: ... > ... |
| test.c:58:9:58:14 | CompareEQ: ... == ... |
| test.c:58:19:58:23 | CompareLT: ... < ... |
| test.c:75:9:75:14 | CompareEQ: ... == ... |
| test.c:85:8:85:13 | CompareEQ: ... == ... |
| test.c:85:18:85:23 | CompareNE: ... != ... |
| test.c:94:11:94:16 | CompareNE: ... != ... |
| test.c:102:16:102:21 | CompareLT: ... < ... |
| test.c:109:9:109:14 | CompareEQ: ... == ... |
| test.c:109:19:109:23 | CompareLT: ... < ... |
| test.c:126:7:126:7 | Constant: 1 |
| test.c:126:12:126:26 | Call: call to test3_condition |
| test.c:131:7:131:7 | Load: b |
| test.c:137:7:137:7 | Constant: 0 |
| test.c:138:9:138:9 | Load: i |
| test.c:146:8:146:8 | Load: x |
| test.c:152:10:152:10 | Load: x |
| test.c:152:15:152:15 | Load: y |
| test.cpp:18:8:18:12 | CompareNE: (bool)... |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... |
| test.cpp:42:13:42:20 | Call: call to getABool |
irGuardsCompare
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
| 7 | x < 0+1 when CompareGT: ... > ... is false |
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
| 17 | 0 < x+1 when CompareLT: ... < ... is false |
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
| 17 | x < 0+0 when CompareLT: ... < ... is true |
| 17 | x >= 0+0 when CompareLT: ... < ... is false |
| 17 | y < 1+1 when CompareGT: ... > ... is false |
| 17 | y >= 1+1 when CompareGT: ... > ... is true |
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
| 26 | x < 0+1 when CompareGT: ... > ... is false |
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
| 34 | j < 10+0 when CompareLT: ... < ... is true |
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
| 42 | j < 10+0 when CompareLT: ... < ... is true |
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
| 44 | z < 0+1 when CompareGT: ... > ... is false |
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
| 45 | y < 0+1 when CompareGT: ... > ... is false |
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
| 58 | 0 != x+0 when CompareEQ: ... == ... is false |
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
| 58 | y < 0+0 when CompareLT: ... < ... is true |
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
| 85 | x == 0+0 when CompareEQ: ... == ... is true |
| 85 | y != 0+0 when CompareNE: ... != ... is true |
| 85 | y == 0+0 when CompareNE: ... != ... is false |
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
| 94 | x != 0+0 when CompareNE: ... != ... is true |
| 94 | x == 0+0 when CompareNE: ... != ... is false |
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
| 102 | j < 10+0 when CompareLT: ... < ... is true |
| 102 | j >= 10+0 when CompareLT: ... < ... is false |
| 109 | 0 != x+0 when CompareEQ: ... == ... is false |
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
| 109 | y < 0+0 when CompareLT: ... < ... is true |
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
irGuardsControl
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
| test.c:7:9:7:13 | CompareGT: ... > ... | true | 8 | 8 |
| test.c:17:8:17:12 | CompareLT: ... < ... | true | 17 | 17 |
| test.c:17:8:17:12 | CompareLT: ... < ... | true | 18 | 18 |
| test.c:17:17:17:21 | CompareGT: ... > ... | true | 18 | 18 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 2 | 2 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 31 | 31 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 34 | 34 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 35 | 35 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 39 | 39 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 42 | 42 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 43 | 43 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 45 | 45 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 46 | 46 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 49 | 49 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 52 | 52 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 56 | 56 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 58 | 58 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 59 | 59 |
| test.c:26:11:26:15 | CompareGT: ... > ... | false | 62 | 62 |
| test.c:26:11:26:15 | CompareGT: ... > ... | true | 27 | 27 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 2 | 2 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 39 | 39 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 42 | 42 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 43 | 43 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 45 | 45 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 46 | 46 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 49 | 49 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 52 | 52 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 56 | 56 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 58 | 58 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 59 | 59 |
| test.c:34:16:34:21 | CompareLT: ... < ... | false | 62 | 62 |
| test.c:34:16:34:21 | CompareLT: ... < ... | true | 35 | 35 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 42 | 42 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 43 | 43 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 45 | 45 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 46 | 46 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 49 | 49 |
| test.c:42:16:42:21 | CompareLT: ... < ... | true | 52 | 52 |
| test.c:44:12:44:16 | CompareGT: ... > ... | false | 52 | 52 |
| test.c:44:12:44:16 | CompareGT: ... > ... | true | 45 | 45 |
| test.c:44:12:44:16 | CompareGT: ... > ... | true | 46 | 46 |
| test.c:44:12:44:16 | CompareGT: ... > ... | true | 49 | 49 |
| test.c:45:16:45:20 | CompareGT: ... > ... | false | 49 | 49 |
| test.c:45:16:45:20 | CompareGT: ... > ... | true | 46 | 46 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | false | 58 | 58 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | false | 62 | 62 |
| test.c:58:19:58:23 | CompareLT: ... < ... | false | 62 | 62 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | false | 79 | 79 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | true | 76 | 76 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | true | 85 | 85 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | true | 86 | 86 |
| test.c:85:18:85:23 | CompareNE: ... != ... | true | 86 | 86 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 70 | 70 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 99 | 99 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 102 | 102 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 103 | 103 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 107 | 107 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 109 | 109 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 110 | 110 |
| test.c:94:11:94:16 | CompareNE: ... != ... | false | 113 | 113 |
| test.c:94:11:94:16 | CompareNE: ... != ... | true | 95 | 95 |
| test.c:102:16:102:21 | CompareLT: ... < ... | false | 70 | 70 |
| test.c:102:16:102:21 | CompareLT: ... < ... | false | 107 | 107 |
| test.c:102:16:102:21 | CompareLT: ... < ... | false | 109 | 109 |
| test.c:102:16:102:21 | CompareLT: ... < ... | false | 110 | 110 |
| test.c:102:16:102:21 | CompareLT: ... < ... | false | 113 | 113 |
| test.c:102:16:102:21 | CompareLT: ... < ... | true | 103 | 103 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | false | 109 | 109 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | false | 113 | 113 |
| test.c:109:19:109:23 | CompareLT: ... < ... | false | 113 | 113 |
| test.c:126:7:126:7 | Constant: 1 | true | 126 | 126 |
| test.c:126:7:126:7 | Constant: 1 | true | 127 | 127 |
| test.c:126:12:126:26 | Call: call to test3_condition | true | 127 | 127 |
| test.c:131:7:131:7 | Load: b | true | 132 | 132 |
| test.c:137:7:137:7 | Constant: 0 | true | 138 | 138 |
| test.c:137:7:137:7 | Constant: 0 | true | 139 | 139 |
| test.c:138:9:138:9 | Load: i | true | 139 | 139 |
| test.c:146:8:146:8 | Load: x | false | 147 | 147 |
| test.c:152:10:152:10 | Load: x | true | 152 | 152 |
| test.c:152:15:152:15 | Load: y | true | 152 | 152 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | true | 0 | 0 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | false | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 30 | 30 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 32 | 32 |
| test.cpp:42:13:42:20 | Call: call to getABool | false | 53 | 53 |
| test.cpp:42:13:42:20 | Call: call to getABool | true | 44 | 44 |
irGuardsEnsure
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | < | test.c:7:13:7:13 | Constant: 0 | 1 | 11 | 11 |
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | >= | test.c:7:13:7:13 | Constant: 0 | 1 | 8 | 8 |
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:13:7:13 | Constant: 0 | < | test.c:7:9:7:9 | Load: x | 0 | 8 | 8 |
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:13:7:13 | Constant: 0 | >= | test.c:7:9:7:9 | Load: x | 0 | 11 | 11 |
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | test.c:17:12:17:12 | Constant: 0 | 0 | 17 | 17 |
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | test.c:17:12:17:12 | Constant: 0 | 0 | 18 | 18 |
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:12:17:12 | Constant: 0 | >= | test.c:17:8:17:8 | Load: x | 1 | 17 | 17 |
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:12:17:12 | Constant: 0 | >= | test.c:17:8:17:8 | Load: x | 1 | 18 | 18 |
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:17:17:17 | Load: y | >= | test.c:17:21:17:21 | Constant: (long)... | 1 | 18 | 18 |
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:21:17:21 | Constant: (long)... | < | test.c:17:17:17:17 | Load: y | 0 | 18 | 18 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 2 | 2 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 31 | 31 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 34 | 34 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 35 | 35 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 39 | 39 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 42 | 42 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 43 | 43 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 45 | 45 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 46 | 46 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 49 | 49 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 52 | 52 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 56 | 56 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 58 | 58 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 59 | 59 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | test.c:26:15:26:15 | Constant: 0 | 1 | 62 | 62 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | >= | test.c:26:15:26:15 | Constant: 0 | 1 | 27 | 27 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | < | test.c:26:11:26:11 | Load: x | 0 | 27 | 27 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 2 | 2 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 31 | 31 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 34 | 34 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 35 | 35 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 39 | 39 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 42 | 42 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 43 | 43 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 45 | 45 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 46 | 46 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 49 | 49 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 52 | 52 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 56 | 56 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 58 | 58 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 59 | 59 |
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:15:26:15 | Constant: 0 | >= | test.c:26:11:26:11 | Load: x | 0 | 62 | 62 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | < | test.c:34:20:34:21 | Constant: 10 | 0 | 35 | 35 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 2 | 2 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 39 | 39 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 42 | 42 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 43 | 43 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 45 | 45 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 46 | 46 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 49 | 49 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 52 | 52 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 56 | 56 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 58 | 58 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 59 | 59 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | test.c:34:20:34:21 | Constant: 10 | 0 | 62 | 62 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 2 | 2 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 39 | 39 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 42 | 42 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 43 | 43 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 45 | 45 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 46 | 46 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 49 | 49 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 52 | 52 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 56 | 56 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 58 | 58 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 59 | 59 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | < | test.c:34:16:34:16 | Load: j | 1 | 62 | 62 |
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:20:34:21 | Constant: 10 | >= | test.c:34:16:34:16 | Load: j | 1 | 35 | 35 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 42 | 42 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 43 | 43 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 45 | 45 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 46 | 46 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 49 | 49 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | test.c:42:20:42:21 | Constant: 10 | 0 | 52 | 52 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 42 | 42 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 43 | 43 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 45 | 45 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 46 | 46 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 49 | 49 |
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:20:42:21 | Constant: 10 | >= | test.c:42:16:42:16 | Load: j | 1 | 52 | 52 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | < | test.c:44:16:44:16 | Constant: 0 | 1 | 52 | 52 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 45 | 45 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 46 | 46 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | test.c:44:16:44:16 | Constant: 0 | 1 | 49 | 49 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 45 | 45 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 46 | 46 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | < | test.c:44:12:44:12 | Load: z | 0 | 49 | 49 |
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:16:44:16 | Constant: 0 | >= | test.c:44:12:44:12 | Load: z | 0 | 52 | 52 |
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | < | test.c:45:20:45:20 | Constant: (long)... | 1 | 49 | 49 |
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | >= | test.c:45:20:45:20 | Constant: (long)... | 1 | 46 | 46 |
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:20:45:20 | Constant: (long)... | < | test.c:45:16:45:16 | Load: y | 0 | 46 | 46 |
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:20:45:20 | Constant: (long)... | >= | test.c:45:16:45:16 | Load: y | 0 | 49 | 49 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | test.c:58:14:58:14 | Constant: 0 | 0 | 58 | 58 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | test.c:58:14:58:14 | Constant: 0 | 0 | 62 | 62 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:14:58:14 | Constant: 0 | != | test.c:58:9:58:9 | Load: x | 0 | 58 | 58 |
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:14:58:14 | Constant: 0 | != | test.c:58:9:58:9 | Load: x | 0 | 62 | 62 |
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:19:58:19 | Load: y | >= | test.c:58:23:58:23 | Constant: (long)... | 0 | 62 | 62 |
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:23:58:23 | Constant: (long)... | < | test.c:58:19:58:19 | Load: y | 1 | 62 | 62 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | != | test.c:75:14:75:14 | Constant: 0 | 0 | 79 | 79 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | test.c:75:14:75:14 | Constant: 0 | 0 | 76 | 76 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | != | test.c:75:9:75:9 | Load: x | 0 | 79 | 79 |
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | == | test.c:75:9:75:9 | Load: x | 0 | 76 | 76 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | test.c:85:13:85:13 | Constant: 0 | 0 | 85 | 85 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | test.c:85:13:85:13 | Constant: 0 | 0 | 86 | 86 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:13:85:13 | Constant: 0 | == | test.c:85:8:85:8 | Load: x | 0 | 85 | 85 |
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:13:85:13 | Constant: 0 | == | test.c:85:8:85:8 | Load: x | 0 | 86 | 86 |
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:18 | Load: y | != | test.c:85:23:85:23 | Constant: (long)... | 0 | 86 | 86 |
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:23:85:23 | Constant: (long)... | != | test.c:85:18:85:18 | Load: y | 0 | 86 | 86 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | != | test.c:94:16:94:16 | Constant: 0 | 0 | 95 | 95 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 70 | 70 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 99 | 99 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 102 | 102 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 103 | 103 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 107 | 107 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 109 | 109 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 110 | 110 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | test.c:94:16:94:16 | Constant: 0 | 0 | 113 | 113 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | != | test.c:94:11:94:11 | Load: x | 0 | 95 | 95 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 70 | 70 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 99 | 99 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 102 | 102 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 103 | 103 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 107 | 107 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 109 | 109 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 110 | 110 |
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:16:94:16 | Constant: 0 | == | test.c:94:11:94:11 | Load: x | 0 | 113 | 113 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | < | test.c:102:20:102:21 | Constant: 10 | 0 | 103 | 103 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | test.c:102:20:102:21 | Constant: 10 | 0 | 70 | 70 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | test.c:102:20:102:21 | Constant: 10 | 0 | 107 | 107 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | test.c:102:20:102:21 | Constant: 10 | 0 | 109 | 109 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | test.c:102:20:102:21 | Constant: 10 | 0 | 110 | 110 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | test.c:102:20:102:21 | Constant: 10 | 0 | 113 | 113 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | < | test.c:102:16:102:16 | Load: j | 1 | 70 | 70 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | < | test.c:102:16:102:16 | Load: j | 1 | 107 | 107 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | < | test.c:102:16:102:16 | Load: j | 1 | 109 | 109 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | < | test.c:102:16:102:16 | Load: j | 1 | 110 | 110 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | < | test.c:102:16:102:16 | Load: j | 1 | 113 | 113 |
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:20:102:21 | Constant: 10 | >= | test.c:102:16:102:16 | Load: j | 1 | 103 | 103 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | test.c:109:14:109:14 | Constant: 0 | 0 | 109 | 109 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | test.c:109:14:109:14 | Constant: 0 | 0 | 113 | 113 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:14:109:14 | Constant: 0 | != | test.c:109:9:109:9 | Load: x | 0 | 109 | 109 |
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:14:109:14 | Constant: 0 | != | test.c:109:9:109:9 | Load: x | 0 | 113 | 113 |
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | test.c:109:23:109:23 | Constant: (long)... | 0 | 113 | 113 |
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:23:109:23 | Constant: (long)... | < | test.c:109:19:109:19 | Load: y | 1 | 113 | 113 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | test.cpp:18:8:18:12 | Constant: (bool)... | 0 | 0 | 0 |
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | Constant: (bool)... | != | test.cpp:18:8:18:10 | Call: call to get | 0 | 0 | 0 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | test.cpp:31:12:31:13 | Constant: - ... | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | test.cpp:31:12:31:13 | Constant: - ... | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | test.cpp:31:12:31:13 | Constant: - ... | 0 | 32 | 32 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 |
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 |

View File

@@ -0,0 +1,91 @@
import cpp
import semmle.code.cpp.controlflow.IRGuards
query predicate astGuards(GuardCondition guard) {
any()
}
query predicate astGuardsCompare(int startLine, string msg) {
exists(GuardCondition guard, Expr left, Expr right, int k, string which, string op |
exists(boolean sense |
sense = true and which = "true"
or sense = false and which = "false"
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
or
guard.comparesLt(left, right, k, false, sense) and op = " >= "
or
guard.comparesEq(left, right, k, true, sense) and op = " == "
or
guard.comparesEq(left, right, k, false, sense) and op = " != "
)
and startLine = guard.getLocation().getStartLine()
and msg = left + op + right + "+" + k + " when " + guard + " is " + which
)
}
query predicate astGuardsControl(GuardCondition guard, boolean sense, int start, int end) {
exists(BasicBlock block |
guard.controls(block, sense) and
block.hasLocationInfo(_, start, _, end, _)
)
}
query predicate astGuardsEnsure(GuardCondition guard, Expr left, string op, Expr right, int k, int start, int end)
{
exists(BasicBlock block |
guard.ensuresLt(left, right, k, block, true) and op = "<"
or
guard.ensuresLt(left, right, k, block, false) and op = ">="
or
guard.ensuresEq(left, right, k, block, true) and op = "=="
or
guard.ensuresEq(left, right, k, block, false) and op = "!="
|
block.hasLocationInfo(_, start, _, end, _)
)
}
query predicate irGuards(IRGuardCondition guard) {
any()
}
query predicate irGuardsCompare(int startLine, string msg) {
exists(IRGuardCondition guard, Instruction left, Instruction right, int k, string which, string op |
exists(boolean sense |
sense = true and which = "true"
or sense = false and which = "false"
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
or
guard.comparesLt(left, right, k, false, sense) and op = " >= "
or
guard.comparesEq(left, right, k, true, sense) and op = " == "
or
guard.comparesEq(left, right, k, false, sense) and op = " != "
)
and startLine = guard.getLocation().getStartLine()
and msg = left.getUnconvertedResultExpression() + op + right.getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " + which
)
}
query predicate irGuardsControl(IRGuardCondition guard, boolean sense, int start, int end) {
exists(IRBlock block |
guard.controls(block, sense) and
block.getLocation().hasLocationInfo(_, start, _, end, _)
)
}
query predicate irGuardsEnsure(IRGuardCondition guard, Instruction left, string op, Instruction right, int k, int start, int end)
{
exists(IRBlock block |
guard.ensuresLt(left, right, k, block, true) and op = "<"
or
guard.ensuresLt(left, right, k, block, false) and op = ">="
or
guard.ensuresEq(left, right, k, block, true) and op = "=="
or
guard.ensuresEq(left, right, k, block, false) and op = "!="
|
block.getLocation().hasLocationInfo(_, start, _, end, _)
)
}

View File

@@ -34,7 +34,6 @@
| test.cpp:8:31:8:35 | initializer for Value |
| test.cpp:11:10:11:11 | mention of EC |
| test.cpp:11:25:11:27 | mention of IsX<X> |
| test.cpp:11:25:11:27 | mention of IsX<X> |
| test.cpp:12:8:12:9 | DX<X, B> |
| test.cpp:12:8:12:9 | definition of DX<X, B> |
| test.cpp:13:17:13:20 | Type |

View File

@@ -32,3 +32,4 @@
| PointlessComparison.c:129:12:129:16 | ... > ... | Comparison is always false because a <= 3. |
| PointlessComparison.c:197:7:197:11 | ... < ... | Comparison is always false because x >= 0. |
| RegressionTests.cpp:57:7:57:22 | ... <= ... | Comparison is always true because * ... <= 4294967295. |
| Templates.cpp:9:10:9:24 | ... <= ... | Comparison is always true because local <= 32767. |

View File

@@ -0,0 +1,17 @@
template<typename T>
bool sometimesPointless(T param) {
return param <= 0xFFFF; // GOOD (hypothetical instantiations are okay)
}
template<typename T>
bool alwaysPointless(T param) {
short local = param;
return local <= 0xFFFF; // BAD (in all instantiations)
}
static int caller(int i) {
return
sometimesPointless<short>(i) ||
alwaysPointless<short>(i) ||
alwaysPointless<int>(i);
}

View File

@@ -1,5 +1,4 @@
| custom_printf.cpp:31:5:31:12 | call to myPrintf | Format expects 2 arguments but given 3 |
| custom_printf.cpp:44:2:44:7 | call to printf | Format expects 0 arguments but given 2 |
| macros.cpp:12:2:12:31 | call to printf | Format expects 2 arguments but given 3 |
| macros.cpp:16:2:16:30 | call to printf | Format expects 2 arguments but given 3 |
| test.c:7:2:7:7 | call to printf | Format expects 0 arguments but given 1 |

View File

@@ -1,5 +1,4 @@
| custom_printf.cpp:29:5:29:12 | call to myPrintf | Format expects 2 arguments but given 1 |
| custom_printf.cpp:45:2:45:7 | call to printf | Format expects 2 arguments but given 0 |
| macros.cpp:14:2:14:37 | call to printf | Format expects 4 arguments but given 3 |
| macros.cpp:21:2:21:36 | call to printf | Format expects 4 arguments but given 3 |
| test.c:9:2:9:7 | call to printf | Format expects 1 arguments but given 0 |

View File

@@ -41,6 +41,6 @@ void test_custom_printf2()
{
// notTheFormat format ...
printf(0, "%i %i", 100, 200); // GOOD
printf("", "%i %i", 100, 200); // GOOD [FALSE POSITIVE]
printf("%i %i", "" ); // GOOD [FALSE POSITIVE]
printf("", "%i %i", 100, 200); // GOOD
printf("%i %i", "" ); // GOOD
}

View File

@@ -0,0 +1,10 @@
| tests.cpp:18:15:18:22 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:19:15:19:22 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:25:17:25:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:31:17:31:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
| tests.cpp:38:36:38:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |

View File

@@ -0,0 +1 @@
Likely Bugs/Format/WrongTypeFormatArguments.ql

View File

@@ -0,0 +1,3 @@
| tests.cpp:8:5:8:10 | printf | char | char16_t, wchar_t | char16_t, wchar_t |
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t |
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t |

View File

@@ -0,0 +1,8 @@
import cpp
from FormattingFunction f
select
f,
concat(f.getDefaultCharType().toString(), ", "),
concat(f.getNonDefaultCharType().toString(), ", "),
concat(f.getWideCharType().toString(), ", ")

View File

@@ -0,0 +1,40 @@
/*
* Test for custom definitions of *wprintf using different types than the
* platform wide character type.
*/
typedef unsigned int size_t;
int printf(const char * format, ...);
int wprintf(const wchar_t * format, ...); // on wchar_t
int swprintf(char16_t * s, size_t n, const char16_t * format, ...); // on char16_t
#define BUF_SIZE (4096)
void tests() {
char16_t buffer[BUF_SIZE];
printf("%s", "Hello"); // GOOD
printf("%s", u"Hello"); // BAD: expecting char
printf("%s", L"Hello"); // BAD: expecting char
printf("%S", "Hello"); // BAD: expecting wchar_t or char16_t [NOT DETECTED]
printf("%S", u"Hello"); // GOOD
printf("%S", L"Hello"); // GOOD
wprintf(L"%s", "Hello"); // BAD: expecting wchar_t
wprintf(L"%s", u"Hello"); // BAD: expecting wchar_t
wprintf(L"%s", L"Hello"); // GOOD
wprintf(L"%S", "Hello"); // GOOD
wprintf(L"%S", u"Hello"); // BAD: expecting char
wprintf(L"%S", L"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // BAD: expecting char
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char
}

View File

@@ -0,0 +1,2 @@
| tests_32.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' |
| tests_64.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' |

View File

@@ -0,0 +1 @@
Likely Bugs/Format/WrongTypeFormatArguments.ql

View File

@@ -0,0 +1,17 @@
// semmle-extractor-options: --edg --target --edg linux_i686
/*
* Test for printf in a snapshot that contains multiple word/pointer sizes.
*/
int printf(const char * format, ...);
void test_32()
{
long l;
void *void_ptr;
printf("%li", l); // GOOD
printf("%li", void_ptr); // BAD
printf("%p", l); // BAD [NOT DETECTED]
printf("%p", void_ptr); // GOOD
}

View File

@@ -0,0 +1,17 @@
// semmle-extractor-options: --edg --target --edg linux_x86_64
/*
* Test for printf in a snapshot that contains multiple word/pointer sizes.
*/
int printf(const char * format, ...);
void test_64()
{
long l;
void *void_ptr;
printf("%li", l); // GOOD
printf("%li", void_ptr); // BAD
printf("%p", l); // BAD [NOT DETECTED]
printf("%p", void_ptr); // GOOD
}

View File

@@ -11,14 +11,6 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:68:19:68:21 | sst | This argument should be of type 'size_t' but is of type 'long' |
| printf1.h:70:19:70:20 | ul | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:83:23:83:35 | ... - ... | This argument should be of type 'size_t' but is of type 'long' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -65,14 +65,14 @@ void g()
printf("%zu", c_st); // ok
printf("%zu", C_ST); // ok
printf("%zu", sizeof(ul)); // ok
printf("%zu", sst); // not ok [NOT DETECTED ON MICROSOFT]
printf("%zu", sst); // not ok [NOT DETECTED]
printf("%zd", ul); // not ok
printf("%zd", st); // not ok
printf("%zd", ST); // not ok
printf("%zd", c_st); // not ok
printf("%zd", C_ST); // not ok
printf("%zd", sizeof(ul)); // not ok
printf("%zd", ul); // not ok [NOT DETECTED]
printf("%zd", st); // not ok [NOT DETECTED]
printf("%zd", ST); // not ok [NOT DETECTED]
printf("%zd", c_st); // not ok [NOT DETECTED]
printf("%zd", C_ST); // not ok [NOT DETECTED]
printf("%zd", sizeof(ul)); // not ok [NOT DETECTED]
printf("%zd", sst); // ok
{
char *ptr_a, *ptr_b;
@@ -80,8 +80,8 @@ void g()
printf("%tu", ptr_a - ptr_b); // ok
printf("%td", ptr_a - ptr_b); // ok
printf("%zu", ptr_a - ptr_b); // ok (dubious) [DETECTED ON LINUX ONLY]
printf("%zd", ptr_a - ptr_b); // ok (dubious) [DETECTED ON MICROSOFT ONLY]
printf("%zu", ptr_a - ptr_b); // ok (dubious)
printf("%zd", ptr_a - ptr_b); // ok (dubious)
}
}
@@ -92,3 +92,12 @@ void h(int i, struct some_type *j, int k)
// going on.
printf("%i %R %i", i, j, k); // GOOD (as far as we can tell)
}
typedef long ptrdiff_t;
void fun1(unsigned char* a, unsigned char* b) {
ptrdiff_t pdt;
printf("%td\n", pdt); // GOOD
printf("%td\n", a-b); // GOOD
}

View File

@@ -0,0 +1,2 @@
| printf.cpp:45:29:45:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
| printf.cpp:52:29:52:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |

View File

@@ -0,0 +1 @@
Likely Bugs/Format/WrongTypeFormatArguments.ql

View File

@@ -0,0 +1,2 @@
| printf.cpp:15:5:15:12 | swprintf | char16_t | char | char16_t |
| printf.cpp:26:5:26:11 | sprintf | char | char16_t | char16_t |

View File

@@ -0,0 +1,8 @@
import cpp
from FormattingFunction f
select
f,
concat(f.getDefaultCharType().toString(), ", "),
concat(f.getNonDefaultCharType().toString(), ", "),
concat(f.getWideCharType().toString(), ", ")

View File

@@ -0,0 +1,53 @@
/*
* Test for custom definitions of *wprintf using different types than the
* platform wide character type.
*/
#define WCHAR char16_t
typedef void *va_list;
#define va_start(va, other)
#define va_end(args)
int vswprintf(WCHAR *dest, WCHAR *format, va_list args) {
return 0;
}
int swprintf(WCHAR *dest, WCHAR *format, ...) {
va_list args;
va_start(args, format);
int ret = vswprintf(dest, format, args);
va_end(args);
return ret;
}
int sprintf(char *dest, char *format, ...);
// ---
void test1() {
WCHAR string[20];
swprintf(string, u"test %s", u"test"); // GOOD
}
void test2() {
char string[20];
sprintf(string, "test %S", u"test"); // GOOD
}
void test3() {
char string[20];
sprintf(string, "test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string
}
void test4() {
char string[20];
sprintf(string, "test %S", L"test"); // BAD: `wchar_t` string parameter read as `char16_t` string
}

View File

@@ -11,14 +11,6 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:68:19:68:21 | sst | This argument should be of type 'size_t' but is of type 'long' |
| printf1.h:70:19:70:20 | ul | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:83:23:83:35 | ... - ... | This argument should be of type 'size_t' but is of type 'long' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -0,0 +1,4 @@
| common.h:12:12:12:17 | printf | char | wchar_t | wchar_t |
| format.h:4:13:4:17 | error | char | wchar_t | wchar_t |
| real_world.h:8:12:8:18 | fprintf | char | wchar_t | wchar_t |
| real_world.h:33:6:33:12 | msg_out | char | wchar_t | wchar_t |

View File

@@ -0,0 +1,8 @@
import cpp
from FormattingFunction f
select
f,
concat(f.getDefaultCharType().toString(), ", "),
concat(f.getNonDefaultCharType().toString(), ", "),
concat(f.getWideCharType().toString(), ", ")

View File

@@ -65,14 +65,14 @@ void g()
printf("%zu", c_st); // ok
printf("%zu", C_ST); // ok
printf("%zu", sizeof(ul)); // ok
printf("%zu", sst); // not ok [NOT DETECTED ON MICROSOFT]
printf("%zu", sst); // not ok [NOT DETECTED]
printf("%zd", ul); // not ok
printf("%zd", st); // not ok
printf("%zd", ST); // not ok
printf("%zd", c_st); // not ok
printf("%zd", C_ST); // not ok
printf("%zd", sizeof(ul)); // not ok
printf("%zd", ul); // not ok [NOT DETECTED]
printf("%zd", st); // not ok [NOT DETECTED]
printf("%zd", ST); // not ok [NOT DETECTED]
printf("%zd", c_st); // not ok [NOT DETECTED]
printf("%zd", C_ST); // not ok [NOT DETECTED]
printf("%zd", sizeof(ul)); // not ok [NOT DETECTED]
printf("%zd", sst); // ok
{
char *ptr_a, *ptr_b;
@@ -80,8 +80,8 @@ void g()
printf("%tu", ptr_a - ptr_b); // ok
printf("%td", ptr_a - ptr_b); // ok
printf("%zu", ptr_a - ptr_b); // ok (dubious) [DETECTED ON LINUX ONLY]
printf("%zd", ptr_a - ptr_b); // ok (dubious) [DETECTED ON MICROSOFT ONLY]
printf("%zu", ptr_a - ptr_b); // ok (dubious)
printf("%zd", ptr_a - ptr_b); // ok (dubious)
}
}
@@ -92,3 +92,12 @@ void h(int i, struct some_type *j, int k)
// going on.
printf("%i %R %i", i, j, k); // GOOD (as far as we can tell)
}
typedef long ptrdiff_t;
void fun1(unsigned char* a, unsigned char* b) {
ptrdiff_t pdt;
printf("%td\n", pdt); // GOOD
printf("%td\n", a-b); // GOOD
}

View File

@@ -11,7 +11,6 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:70:19:70:20 | ul | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |

View File

@@ -65,9 +65,9 @@ void g()
printf("%zu", c_st); // ok
printf("%zu", C_ST); // ok
printf("%zu", sizeof(ul)); // ok
printf("%zu", sst); // not ok [NOT DETECTED ON MICROSOFT]
printf("%zu", sst); // not ok [NOT DETECTED]
printf("%zd", ul); // not ok
printf("%zd", ul); // not ok [NOT DETECTED]
printf("%zd", st); // not ok
printf("%zd", ST); // not ok
printf("%zd", c_st); // not ok
@@ -80,8 +80,8 @@ void g()
printf("%tu", ptr_a - ptr_b); // ok
printf("%td", ptr_a - ptr_b); // ok
printf("%zu", ptr_a - ptr_b); // ok (dubious) [DETECTED ON LINUX ONLY]
printf("%zd", ptr_a - ptr_b); // ok (dubious) [DETECTED ON MICROSOFT ONLY]
printf("%zu", ptr_a - ptr_b); // ok (dubious)
printf("%zd", ptr_a - ptr_b); // ok (dubious) [FALSE POSITIVE]
}
}
@@ -92,3 +92,12 @@ void h(int i, struct some_type *j, int k)
// going on.
printf("%i %R %i", i, j, k); // GOOD (as far as we can tell)
}
typedef long long ptrdiff_t;
void fun1(unsigned char* a, unsigned char* b) {
ptrdiff_t pdt;
printf("%td\n", pdt); // GOOD
printf("%td\n", a-b); // GOOD
}

View File

@@ -43,7 +43,7 @@ void someFunction()
WCHAR filename[MAX_LONGPATH];
int linenum;
msg_out("Source file: %S @ %d\n", filename, linenum); // GOOD
msg_out("Source file: %S @ %d\n", filename, linenum); // GOOD [FALSE POSITIVE]
}
// --------------------------------------------------------------

View File

@@ -11,13 +11,13 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:70:19:70:20 | ul | This argument should be of type 'ssize_t' but is of type 'unsigned long' |
| printf1.h:71:19:71:20 | st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:72:19:72:20 | ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:73:19:73:22 | c_st | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:84:23:84:35 | ... - ... | This argument should be of type 'ssize_t' but is of type 'long long' |
| printf1.h:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -65,9 +65,9 @@ void g()
printf("%zu", c_st); // ok
printf("%zu", C_ST); // ok
printf("%zu", sizeof(ul)); // ok
printf("%zu", sst); // not ok [NOT DETECTED ON MICROSOFT]
printf("%zu", sst); // not ok [NOT DETECTED]
printf("%zd", ul); // not ok
printf("%zd", ul); // not ok [NOT DETECTED]
printf("%zd", st); // not ok
printf("%zd", ST); // not ok
printf("%zd", c_st); // not ok
@@ -80,8 +80,8 @@ void g()
printf("%tu", ptr_a - ptr_b); // ok
printf("%td", ptr_a - ptr_b); // ok
printf("%zu", ptr_a - ptr_b); // ok (dubious) [DETECTED ON LINUX ONLY]
printf("%zd", ptr_a - ptr_b); // ok (dubious) [DETECTED ON MICROSOFT ONLY]
printf("%zu", ptr_a - ptr_b); // ok (dubious)
printf("%zd", ptr_a - ptr_b); // ok (dubious) [FALSE POSITIVE]
}
}
@@ -92,3 +92,40 @@ void h(int i, struct some_type *j, int k)
// going on.
printf("%i %R %i", i, j, k); // GOOD (as far as we can tell)
}
typedef long long ptrdiff_t;
void fun1(unsigned char* a, unsigned char* b) {
ptrdiff_t pdt;
printf("%td\n", pdt); // GOOD
printf("%td\n", a-b); // GOOD
}
typedef wchar_t WCHAR_T; // WCHAR_T -> wchar_t -> int
typedef int MYCHAR; // MYCHAR -> int (notably not via the wchar_t typedef)
void fun2() {
wchar_t *myString1;
WCHAR_T *myString2;
int *myString3;
MYCHAR *myString4;
printf("%S", myString1); // GOOD
printf("%S", myString2); // GOOD
printf("%S", myString3); // GOOD
printf("%S", myString4); // GOOD
}
typedef void *VOIDPTR;
typedef int (*FUNPTR)(int);
void fun3(void *p1, VOIDPTR p2, FUNPTR p3, char *p4)
{
printf("%p\n", p1); // GOOD
printf("%p\n", p2); // GOOD
printf("%p\n", p3); // GOOD
printf("%p\n", p4); // GOOD
printf("%p\n", p4 + 1); // GOOD
printf("%p\n", 0); // GOOD [FALSE POSITIVE]
}

View File

@@ -171,3 +171,10 @@ void more_tests_2()
memset(iapa, 0, sizeof(iapa)); // GOOD
memset(iapa, 0, sizeof(intArrayPointer *)); // BAD
}
void more_tests_3()
{
myStruct ms;
decltype(&ms) msPtr = &ms;
memset(msPtr, 0, sizeof(myStruct)); // GOOD
}

View File

@@ -0,0 +1,34 @@
#define NULL 0
#define CONST const
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef char CHAR;
typedef WCHAR *LPWSTR;
typedef CONST WCHAR *LPCWSTR;
typedef CHAR *LPSTR;
typedef CONST CHAR *LPCSTR;
void fconstWChar(LPCWSTR p) {}
void fWChar(LPWSTR p) {}
void Test()
{
char *lpChar = NULL;
wchar_t *lpWchar = NULL;
LPCSTR lpcstr = "b";
lpWchar = (LPWSTR)"a"; // BUG
lpWchar = (LPWSTR)lpcstr; // BUG
lpWchar = (wchar_t*)lpChar; // BUG
fconstWChar((LPCWSTR)lpChar); // BUG
fWChar((LPWSTR)lpChar); // BUG
lpChar = (LPSTR)"a"; // Valid
lpWchar = (LPWSTR)L"a"; // Valid
fconstWChar((LPCWSTR)lpWchar); // Valid
fWChar(lpWchar); // Valid
}

View File

@@ -0,0 +1,5 @@
| WcharCharConversion.cpp:21:20:21:22 | array to pointer conversion | Conversion from const char * to LPWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:22:20:22:25 | lpcstr | Conversion from LPCSTR to LPWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:24:22:24:27 | lpChar | Conversion from char * to wchar_t *. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:26:23:26:28 | lpChar | Conversion from char * to LPCWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:27:17:27:22 | lpChar | Conversion from char * to LPWSTR. Use of invalid string can lead to undefined behavior. |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-704/WcharCharConversion.ql

View File

@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Semmle C# Default Queries
Bundle-SymbolicName: com.semmle.plugin.semmlecode.csharp.queries;singleton:=true
Bundle-Version: 1.18.0.qualifier
Bundle-Version: 1.18.1.qualifier
Bundle-Vendor: Semmle Ltd.
Bundle-ActivationPolicy: lazy
Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.0.qualifier, 1.18.0.qualifier]"
Require-Bundle: com.semmle.plugin.qdt.ui;bundle-version="[1.18.1.qualifier,1.18.1.qualifier]"

View File

@@ -1 +0,0 @@
<queries language="csharp"/>

View File

@@ -0,0 +1,31 @@
/*
* Tool to generate C# stubs from a qltest snapshot.
*
* It finds all declarations used in the source code,
* and generates minimal C# stubs containing those declarations
* and their dependencies.
*/
import csharp
import Stubs
/** Declarations used by source code. */
class UsedInSource extends GeneratedDeclaration {
UsedInSource() {
(
this = any(Access a).getTarget()
or
this = any(Call c).getTarget()
or
this = any(TypeMention tm).getType()
or
exists(Virtualizable v | v.fromSource() | this = v.getImplementee() or this = v.getOverridee())
or
this = any(Attribute a).getType().getAConstructor()
)
and
this.fromLibrary()
}
}
select generatedCode()

View File

@@ -0,0 +1,472 @@
/**
* Generates C# stubs for use in test code.
*
* Extend the abstract class `GeneratedDeclaration` with the declarations that should be generated.
* This will generate stubs for all the required dependencies as well.
*
* Use
* ```
* select generatedCode()
* ```
* to retrieve the generated C# code.
*/
import csharp
/** An element that should be in the generated code. */
abstract class GeneratedElement extends Element {
}
/** A member that should be in the generated code. */
abstract class GeneratedMember extends Member, GeneratedElement {
}
/** A type that should be in the generated code. */
private abstract class GeneratedType extends ValueOrRefType, GeneratedElement {
GeneratedType() {
(
this instanceof Interface
or
this instanceof Class
or
this instanceof Struct
or
this instanceof Enum
or
this instanceof DelegateType
)
and
not this instanceof ConstructedType
and
not this.getALocation() instanceof ExcludedAssembly
and
this.fromLibrary()
}
/**
* Holds if this type is duplicated in another assembly.
* In this case, we use the assembly with the highest string.
*/
private predicate isDuplicate() {
exists(GeneratedType dup |
dup.getQualifiedName() = this.getQualifiedName() and
this.getLocation().(Assembly).toString() < dup.getLocation().(Assembly).toString()
)
}
private string stubKeyword() {
this instanceof Interface and result = "interface"
or
this instanceof Struct and result = "struct"
or
this instanceof Class and result = "class"
or
this instanceof Enum and result = "enum"
}
private string stubAbstractModifier() {
if this.(Class).isAbstract() then
result = "abstract "
else
result = ""
}
private string stubStaticModifier() {
if this.isStatic() then result = "static "
else result = ""
}
private string stubAttributes() {
if this.getAnAttribute().getType().getQualifiedName() = "System.FlagsAttribute" then
result = "[System.Flags]\n"
else
result = ""
}
private string stubComment() {
result = "// Generated from `" + this.getQualifiedName() + "` in `" +
min(this.getLocation().toString()) + "`\n"
}
private string stubAccessibilityModifier() {
if this.isPublic() then
result = "public "
else
result = ""
}
/** Gets the entire C# stub code for this type. */
final string getStub() {
if this.isDuplicate() then
result = ""
else
result = this.stubComment() +
this.stubAttributes() +
this.stubAbstractModifier() +
this.stubStaticModifier() +
this.stubAccessibilityModifier() +
this.stubKeyword() + " " +
this.getUndecoratedName() +
stubGenericArguments(this) +
stubBaseTypesString() +
"\n{\n" +
stubMembers() +
"}\n\n"
}
private ValueOrRefType getAnInterestingBaseType() {
result = this.getABaseType() and
not result instanceof ObjectType and
not result.getQualifiedName() = "System.ValueType"
}
private string stubBaseTypesString() {
if this instanceof Enum then
result = ""
else if exists(getAnInterestingBaseType()) then
result = " : " +
concat(int i, ValueOrRefType t |
t = this.getAnInterestingBaseType() and (if t instanceof Class then i=0 else i=1) |
stubClassName(t), ", " order by i
)
else
result = ""
}
private string stubMembers() {
result = concat(stubMember(this.getAGeneratedMember()))
}
private GeneratedMember getAGeneratedMember() {
result.getDeclaringType() = this
}
final Type getAGeneratedType() {
result = getAnInterestingBaseType()
or
result = getAGeneratedMember().(Callable).getReturnType()
or
result = getAGeneratedMember().(Callable).getAParameter().getType()
or
result = getAGeneratedMember().(Property).getType()
or
result = getAGeneratedMember().(Field).getType()
}
}
/**
* A declaration that should be generated.
* This is extended in client code to identify the actual
* declarations that should be generated.
*/
abstract class GeneratedDeclaration extends Declaration {
}
private class IndirectType extends GeneratedType {
IndirectType() {
this.getASubType() instanceof GeneratedType
or
this.getAChildType() instanceof GeneratedType
or
this.(UnboundGenericType).getAConstructedGeneric().getASubType() instanceof GeneratedType
or
exists(GeneratedType t | this = getAContainedType(t.getAGeneratedType()).getSourceDeclaration())
or
exists(GeneratedDeclaration decl | decl.(Member).getDeclaringType().getSourceDeclaration() = this)
}
}
private class RootGeneratedType extends GeneratedType {
RootGeneratedType() {
this = any(GeneratedDeclaration decl).getSourceDeclaration()
}
}
private Type getAContainedType(Type t) {
result = t
or
result = getAContainedType(t.(ConstructedType).getATypeArgument())
}
private class RootGeneratedMember extends GeneratedMember {
RootGeneratedMember() {
this = any(GeneratedDeclaration d).getSourceDeclaration()
}
}
private predicate declarationExists(Virtualizable m) {
m instanceof GeneratedMember
or
m.getLocation() instanceof ExcludedAssembly
}
private class InheritedMember extends GeneratedMember, Virtualizable {
InheritedMember() {
declarationExists(this.getImplementee+())
or
declarationExists(this.getAnImplementor+())
or
declarationExists(this.getOverridee+())
or
declarationExists(this.getAnOverrider+())
}
}
/** A namespace that contains at least one generated type. */
private class GeneratedNamespace extends Namespace, GeneratedElement {
GeneratedNamespace() {
this.getATypeDeclaration() instanceof GeneratedType
or
this.getAChildNamespace() instanceof GeneratedNamespace
}
private string getPreamble() {
if this.isGlobalNamespace() then
result = ""
else
result = "namespace " + this.getName() + "\n{\n"
}
private string getPostAmble() {
if this.isGlobalNamespace() then
result = ""
else
result = "}\n"
}
final string getStubs() {
result = getPreamble() +
getTypeStubs() +
getSubNamespaces() +
getPostAmble()
}
/** Gets the `n`th generated child namespace, indexed from 0. */
pragma[nomagic]
final GeneratedNamespace getChildNamespace(int n) {
result.getParentNamespace() = this and
result.getName() = rank[n+1](GeneratedNamespace g | g.getParentNamespace() = this | g.getName())
}
final int getChildNamespaceCount() {
result = count(GeneratedNamespace g | g.getParentNamespace() = this)
}
language[monotonicAggregates]
private string getSubNamespaces() {
result = concat(int i | exists(getChildNamespace(i)) | getChildNamespace(i).getStubs())
}
private string getTypeStubs() {
result = concat(string s | s = any(GeneratedType gt | gt.getDeclaringNamespace() = this).getStub())
}
}
/**
* Specify assemblies to exclude.
* Do not generate any types from these assemblies.
*/
abstract class ExcludedAssembly extends Assembly {
}
/** Exclude types from these standard assemblies. */
private class DefaultLibs extends ExcludedAssembly {
DefaultLibs() {
this.getName() = "System.Private.CoreLib"
or this.getName() = "mscorlib"
or this.getName() = "System.Runtime"
}
}
private string stubAccessibility(Member m) {
if m.getDeclaringType() instanceof Interface or exists(m.(Virtualizable).getExplicitlyImplementedInterface()) then result = ""
else if m.isPublic() then result = "public "
else if m.isProtected() then result = "protected "
else if m.isPrivate() then result = "private "
else if m.isInternal() then result = "internal "
else result = "unknown-accessibility"
}
private string stubModifiers(Member m) {
result = stubAccessibility(m) + stubStatic(m) + stubOverride(m)
}
private string stubStatic(Member m) {
if m.(Modifiable).isStatic() then result = "static "
else result = ""
}
private string stubOverride(Member m) {
if m.getDeclaringType() instanceof Interface then result = ""
else if m.(Virtualizable).isVirtual() then result = "virtual "
else if m.(Virtualizable).isAbstract() then result = "abstract "
else if m.(Virtualizable).isOverride() then result = "override "
else result = ""
}
private string stubQualifiedNamePrefix(ValueOrRefType t) {
if t.getParent() instanceof GlobalNamespace then result=""
else if t.getParent() instanceof Namespace then result = t.getParent().(Namespace).getQualifiedName() + "."
else result = stubQualifiedNamePrefix(t.getParent()) + "."
}
language[monotonicAggregates]
private string stubClassName(Type t) {
if t instanceof ObjectType then
result = "object"
else if t instanceof StringType then
result = "string"
else if t instanceof IntType then
result = "int"
else if t instanceof BoolType then
result = "bool"
else if t instanceof VoidType then
result = "void"
else if t instanceof FloatType then
result = "float"
else if t instanceof DoubleType then
result = "double"
else if t instanceof NullableType then
result = stubClassName(t.(NullableType).getUnderlyingType()) + "?"
else if t instanceof TypeParameter then
result = t.getName()
else if t instanceof ArrayType then
result = stubClassName(t.(ArrayType).getElementType()) + "[]"
else if t instanceof PointerType then
result = stubClassName(t.(PointerType).getReferentType()) + "*"
else if t instanceof TupleType then
result = "(" + concat(int i, Type element | element = t.(TupleType).getElementType(i) | stubClassName(element), "," order by i) + ")"
else if t instanceof ValueOrRefType then
result = stubQualifiedNamePrefix(t) + t.getUndecoratedName() + stubGenericArguments(t)
else
result = "<error>"
}
language[monotonicAggregates]
private string stubGenericArguments(ValueOrRefType t) {
if t instanceof UnboundGenericType then
result = "<" + concat(int n | exists(t.(UnboundGenericType).getTypeParameter(n)) | t.(UnboundGenericType).getTypeParameter(n).getName(),"," order by n) + ">"
else if t instanceof ConstructedType then
result = "<" + concat(int n | exists(t.(ConstructedType).getTypeArgument(n)) | stubClassName(t.(ConstructedType).getTypeArgument(n)),"," order by n) + ">"
else
result = ""
}
private string stubGenericMethodParams(Method m) {
if m instanceof UnboundGenericMethod then
result = "<" + concat(int n, TypeParameter param | param = m.(UnboundGenericMethod).getTypeParameter(n) | param.getName(), "," order by n) + ">"
else
result = ""
}
private string stubImplementation(Virtualizable c) {
if c.isAbstract() or c.getDeclaringType() instanceof Interface
then result = ""
else result = " => throw null"
}
private string stubParameters(Parameterizable p) {
result = concat(int i, Parameter param | param = p.getParameter(i) and not param.getType() instanceof ArglistType |
stubParameterModifiers(param) + stubClassName(param.getType()) + " " + param.getName() + stubDefaultValue(param), ", " order by i)
}
private string stubParameterModifiers(Parameter p) {
if p.isOut() then result = "out "
else if p.isRef() then result = "ref "
else if p.isParams() then result = "params "
else if p.isIn() then result = "" // Only C# 7.1 so ignore
else if p.hasExtensionMethodModifier() then result = "this "
else result = ""
}
private string stubDefaultValue(Parameter p) {
if p.hasDefaultValue()
then result = " = default(" + stubClassName(p.getType()) + ")"
else result = ""
}
private string stubExplicitImplementation(Member c) {
if exists(c.(Virtualizable).getExplicitlyImplementedInterface())
then result = stubClassName(c.(Virtualizable).getExplicitlyImplementedInterface()) + "."
else result = ""
}
private string stubMember(Member m) {
exists(Method c | m = c and not m.getDeclaringType() instanceof Enum |
result = " " + stubModifiers(c) + stubClassName(c.getReturnType()) + " " +
stubExplicitImplementation(c) + c.getName() + stubGenericMethodParams(c) +
"(" + stubParameters(c) + ")" + stubImplementation(c) + ";\n"
)
or
exists(Operator op | m = op and not m.getDeclaringType() instanceof Enum and not op instanceof ConversionOperator |
result = " " + stubModifiers(op) + stubClassName(op.getReturnType()) + " operator " +
op.getName() +"(" + stubParameters(op) + ") => throw null;\n"
)
or
exists(ConversionOperator op | m = op |
result = " " + stubModifiers(op) + stubExplicit(op) + "operator " +
stubClassName(op.getReturnType()) + "(" + stubParameters(op) +
") => throw null;\n"
)
or
result = " " + m.(EnumConstant).getName() + ",\n"
or
exists(Property p | m = p |
result = " " + stubModifiers(m) + stubClassName(p.getType()) + " " +
stubExplicitImplementation(p) + p.getName() + " { " + stubGetter(p) + stubSetter(p) + "}\n"
)
or exists(Constructor c | m = c and not c.getDeclaringType() instanceof Enum |
result = " " + stubModifiers(m) + c.getName() + "(" + stubParameters(c) +
") => throw null;\n"
)
or exists(Indexer i | m = i |
result = " " + stubModifiers(m) + stubClassName(i.getType()) + " this[" + stubParameters(i) +
"] { " + stubGetter(i) + stubSetter(i) + "}\n"
)
or exists(Field f | f = m and not f instanceof EnumConstant |
result = " " + stubModifiers(m) + stubClassName(f.getType()) + " " + f.getName() + ";\n"
)
}
private string stubExplicit(ConversionOperator op) {
op instanceof ImplicitConversionOperator and result = "implicit "
or
op instanceof ExplicitConversionOperator and result = "explicit "
}
private string stubGetter(DeclarationWithGetSetAccessors p) {
if exists(p.getGetter()) then (
if p.isAbstract() or p.getDeclaringType() instanceof Interface then
result = "get; "
else
result = "get => throw null; "
) else
result = ""
}
private string stubSetter(DeclarationWithGetSetAccessors p) {
if exists(p.getSetter()) then (
if p.isAbstract() or p.getDeclaringType() instanceof Interface then
result = "set; "
else
result = "set => throw null; "
) else
result = ""
}
private string stubSemmleExtractorOptions() {
result = concat(string s |
exists(CommentLine comment |
s = "// original-extractor-options:" + comment.getText().regexpCapture("\\w*semmle-extractor-options:(.*)", 1) + "\n"
)
)
}
/** Gets the generated C# code. */
string generatedCode() {
result = "// This file contains auto-generated code.\n" +
stubSemmleExtractorOptions() + "\n" +
any(GeneratedNamespace ns | ns.isGlobalNamespace()).getStubs()
}

View File

@@ -0,0 +1,114 @@
# Tool to generate 'stub.cs' files inside C# qltest projects.
# The purpose of this is to stub out assemblies from the qltest case
# so that the test is self-contained and will still run without them.
#
# To do this, it
# 1. Performs a regular qltest to generate a snapshot
# 2. Runs a QL query on the snapshot to find out which symbols are needed,
# then uses QL to generate a string of C# code.
# 3. Re-runs the test to ensure that it still compiles and passes.
import sys
import os
import subprocess
print('Script to generate stub.cs files for C# qltest projects')
if len(sys.argv) < 2:
print("Please supply a qltest directory.")
exit(1)
testDir = sys.argv[1]
if not os.path.isdir(testDir):
print("Directory", testDir, "does not exist")
exit(1)
# Does it contain a .ql file and a .cs file?
foundCS = False
foundQL = False
for file in os.listdir(testDir):
if file.endswith(".cs"):
foundCS = True
if file.endswith(".ql") or file.endswith(".qlref"):
foundQL = True
if not foundQL:
print("Test directory does not contain .ql files. Please specify a working qltest directory.")
exit(1)
if not foundCS:
print("Test directory does not contain .cs files. Please specify a working qltest directory.")
exit(1)
cmd = ['odasa', 'selfTest']
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
print("odasa selfTest failed. Ensure odasa is on your current path.")
exit(1)
csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0]))
outputFile = os.path.join(testDir, 'stubs.cs')
print("Stubbing qltest in", testDir)
if os.path.isfile(outputFile):
os.remove(outputFile) # It would interfere with the test.
print("Removed previous", outputFile)
cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
print("qltest failed. Please fix up the test before proceeding.")
exit(1)
dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp")
if not os.path.isdir(dbDir):
print("Expected database directory " + dbDir + " not found. Please contact Semmle.")
exit(1)
cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
print('Failed to run the query to generate output file. Please contact Semmle.')
exit(1)
# Remove the leading " and trailing " bytes from the file
len = os.stat(outputFile).st_size
f = open(outputFile, "rb")
try:
quote = f.read(1)
if quote != b'"':
print("Unexpected character in file. Please contact Semmle.")
contents = f.read(len-3)
quote = f.read(1)
if quote != b'"':
print("Unexpected end character. Please contact Semmle.", quote)
finally:
f.close()
f = open(outputFile, "wb")
f.write(contents)
f.close()
cmd = ['odasa', 'qltest', '--optimize', testDir]
print('Running ' + ' '.join(cmd))
if subprocess.check_call(cmd):
print('\nTest failed. You may need to fix up', outputFile)
print('It may help to view', outputFile, ' in Visual Studio')
print("Next steps:")
print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles')
print('2. Re-run odasa qltest --optimize "' + testDir + '"')
print('3. git add "' + outputFile + '"')
exit(1)
print("\nStub generation successful! Next steps:")
print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references')
print('2. Re-run odasa qltest --optimize "' + testDir + '"')
print('3. git add "' + outputFile + '"')
print('4. Commit your changes.')
exit(0)

View File

@@ -64,6 +64,20 @@ private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i,
result = ct.getTypeArgument(i)
}
/** A type that is an element type of an array type. */
private class ArrayElementType extends Type {
ArrayElementType() {
this = any(ArrayType at).getElementType()
}
}
/** A type that is an argument in a constructed type. */
private class TypeArgument extends Type {
TypeArgument() {
this = any(ConstructedType ct).getATypeArgument()
}
}
/**
* INTERNAL: Do not use.
*
@@ -78,125 +92,214 @@ private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i,
predicate convIdentity(Type fromType, Type toType) {
fromType = toType
or
convIdentityStrict(fromType, toType)
Identity::convIdentityStrict(fromType, toType)
}
private class IdentityConvertibleType extends Type {
IdentityConvertibleType() {
isIdentityConvertible(this)
private module Identity {
private class IdentityConvertibleType extends Type {
IdentityConvertibleType() {
isIdentityConvertible(this)
}
}
}
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
private class IdentityConvertibleConstructedType extends IdentityConvertibleType {
IdentityConvertibleConstructedType() {
this instanceof ConstructedType
}
}
private class IdentityConvertibleConstructedType extends IdentityConvertibleType, ConstructedType { }
/**
* A type is (strictly) identity convertible if it contains at least one `object`
* or one `dynamic` sub term.
*/
private predicate isIdentityConvertible(Type t) {
t instanceof ObjectType
or
t instanceof DynamicType
or
isIdentityConvertible(t.(ArrayType).getElementType())
or
isIdentityConvertible(t.(ConstructedType).getATypeArgument())
}
private predicate convIdentityStrict(IdentityConvertibleType fromType, IdentityConvertibleType toType) {
convIdentityObjectDynamic(fromType, toType)
or
convIdentityObjectDynamic(toType, fromType)
or
convIdentityStrictArrayType(fromType, toType)
or
convIdentityStrictConstructedType(fromType, toType)
}
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) {
any()
}
private predicate convIdentityStrictArrayType(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType) {
convIdentityStrictArrayTypeJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma [noinline]
private predicate convIdentityStrictArrayTypeJoin(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk) {
convIdentityStrictArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
private predicate convIdentityStrictArrayElementType(IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk) {
convIdentityStrict(fromType.getElementType(), aet) and
dim = fromType.getDimension() and
rnk = fromType.getRank()
}
/** A type that is an element type of an array type. */
private class ArrayElementType extends Type {
ArrayElementType() {
this = any(ArrayType at).getElementType()
}
}
private predicate convIdentityStrictConstructedType(IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
* and
* forex(int i |
* i in [0 .. ugt.getNumberOfTypeParameters() - 1] |
* exists(Type t1, Type t2 |
* t1 = getTypeArgument(ugt, fromType, i, _) and
* t2 = getTypeArgument(ugt, toType, i, _) |
* convIdentity(t1, t2)
* )
* )
* ```
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
/**
* A type is (strictly) identity convertible if it contains at least one `object`
* or one `dynamic` sub term.
*/
exists(UnboundGenericType ugt |
convIdentityStrictConstructedTypeFromZero(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
/**
* Holds if the type arguments 0 through `i` of `fromType` and `toType` are identity convertible.
*/
private predicate convIdentityStrictConstructedTypeFromZero(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
exists(Type toTypeArgument |
convIdentityStrictConstructedTypeFromZeroAux(ugt, fromType, i, toTypeArgument) and
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
fromType != toType |
i = 0
private predicate isIdentityConvertible(Type t) {
t instanceof ObjectType
or
convIdentityStrictConstructedTypeFromZero(ugt, fromType, toType, i - 1)
)
}
t instanceof DynamicType
or
isIdentityConvertible(t.(ArrayType).getElementType())
or
isIdentityConvertible(t.(ConstructedType).getATypeArgument())
}
pragma [nomagic]
private predicate convIdentityStrictConstructedTypeFromZeroAux(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, int i, Type toTypeArgument) {
exists(Type fromTypeArgument |
fromTypeArgument = getTypeArgument(ugt, fromType, i, _) and
convIdentityTypeArgument(fromTypeArgument, toTypeArgument)
)
}
predicate convIdentityStrict(IdentityConvertibleType fromType, IdentityConvertibleType toType) {
convIdentityObjectDynamic(fromType, toType)
or
convIdentityObjectDynamic(toType, fromType)
or
convIdentityStrictArrayType(fromType, toType)
or
convIdentityStrictConstructedType(fromType, toType)
}
private predicate convIdentityTypeArgument(TypeArgument fromType, TypeArgument toType) {
convIdentity(fromType, toType)
}
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) {
any()
}
/** A type that is an argument in a constructed type. */
private class TypeArgument extends Type {
TypeArgument() {
this = any(ConstructedType ct).getATypeArgument()
private predicate convIdentityStrictArrayType(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType) {
convIdentityStrictArrayTypeJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma[noinline]
private predicate convIdentityStrictArrayTypeJoin(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk) {
convIdentityStrictArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
private predicate convIdentityStrictArrayElementType(IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk) {
convIdentityStrict(fromType.getElementType(), aet) and
dim = fromType.getDimension() and
rnk = fromType.getRank()
}
/**
* Gets the number of different type arguments supplied for the type
* parameter at index `i` in unbound generic type `ugt`.
*/
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
result = strictcount(Type arg |
exists(IdentityConvertibleConstructedType ct |
ct.getUnboundGeneric() = ugt |
arg = ct.getTypeArgument(i)
)
)
}
private int rnk(UnboundGenericType ugt, int i) {
result = rank[i + 1](int j, int k |
j = getTypeArgumentCount(ugt, k) |
k order by j, k
)
}
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
private Type getTypeArgumentRanked(UnboundGenericType ugt, IdentityConvertibleConstructedType t, int i) {
result = getTypeArgument(ugt, t, rnk(ugt, i), _)
}
/**
* Holds if `fromTypeArgument` is identity convertible to `toTypeArgument`, and
* both types are the `i`th type argument in _some_ constructed type.
*/
pragma[nomagic]
private predicate convTypeArguments(Type fromTypeArgument, Type toTypeArgument, int i) {
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i) and
toTypeArgument = getTypeArgumentRanked(_, _, j) and
i <= j and j <= i
|
convIdentity(fromTypeArgument, toTypeArgument)
)
}
pragma[nomagic]
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArguments(fromTypeArgument, toTypeArgument, i) and
fromTypeArgument = getTypeArgumentRanked(ugt, _, i)
}
/**
* Holds if `fromTypeArgument` is identity convertible to `toTypeArgument` and
* both types are the `i`th type argument in _some_ constructed type
* based on unbound generic type `ugt`.
*/
pragma[noinline]
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, _, i)
}
pragma[nomagic]
private predicate convIdentitySingle0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0) and
ugt.getNumberOfTypeParameters() = 1
}
/**
* Holds if the type arguments of types `fromType` and `toType` are identity
* convertible, and the number of type arguments is 1.
*/
predicate convIdentitySingle(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convIdentitySingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0)
)
}
pragma[nomagic]
private predicate convIdentityMultiple01Aux0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0) and
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1)
}
pragma[nomagic]
private predicate convIdentityMultiple01Aux1(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0) and
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1)
}
/**
* Holds if the first two ranked type arguments of types `fromType` and `toType`
* are identity convertible.
*/
private predicate convIdentityMultiple01(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
exists(Type fromTypeArgument0, Type toTypeArgument0, Type fromTypeArgument1, Type toTypeArgument1 |
convIdentityMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
convIdentityMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
)
}
pragma[nomagic]
private predicate convIdentityMultiple2Aux(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, i) and
i >= 2
}
private predicate convIdentityMultiple2(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convIdentityMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i)
)
}
/**
* Holds if the ranked type arguments 0 through `i` (with `i >= 1`) of types
* `fromType` and `toType` are identity convertible.
*/
pragma[nomagic]
predicate convIdentityMultiple(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
convIdentityMultiple01(ugt, fromType, toType) and i = 1
or
convIdentityMultiple(ugt, fromType, toType, i - 1) and
convIdentityMultiple2(ugt, fromType, toType, i)
}
private predicate convIdentityStrictConstructedType(IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
* and
* forex(int i |
* i in [0 .. ugt.getNumberOfTypeParameters() - 1] |
* exists(Type t1, Type t2 |
* t1 = getTypeArgument(ugt, fromType, i, _) and
* t2 = getTypeArgument(ugt, toType, i, _) |
* convIdentity(t1, t2)
* )
* )
* ```
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
*/
fromType != toType and
(
convIdentitySingle(_, fromType, toType)
or
exists(UnboundGenericType ugt |
convIdentityMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
)
}
}
@@ -363,9 +466,8 @@ private predicate convRefTypeNonNull(Type fromType, Type toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultDynamicConversion(Type fromType, Type toType) {
pragma[noinline]
private predicate defaultDynamicConversion(Type fromType, Type toType) {
fromType instanceof RefType and toType instanceof DynamicType
}
@@ -373,9 +475,8 @@ predicate defaultDynamicConversion(Type fromType, Type toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultDelegateConversion(RefType fromType, RefType toType) {
pragma[noinline]
private predicate defaultDelegateConversion(RefType fromType, RefType toType) {
fromType instanceof DelegateType and toType = any(SystemDelegateClass c).getABaseType*()
}
@@ -398,9 +499,8 @@ private predicate convRefTypeRefType(RefType fromType, RefType toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultArrayConversion(Type fromType, RefType toType) {
pragma[noinline]
private predicate defaultArrayConversion(Type fromType, RefType toType) {
fromType instanceof ArrayType and toType = any(SystemArrayClass c).getABaseType*()
}
@@ -427,7 +527,7 @@ private predicate convArrayTypeCovariance(ArrayType fromType, ArrayType toType)
convArrayTypeCovarianceJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma [noinline]
pragma[noinline]
private predicate convArrayTypeCovarianceJoin(ArrayType fromType, ArrayType toType, int dim, int rnk) {
convArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
@@ -616,7 +716,7 @@ predicate convConversionOperator(Type fromType, Type toType) {
}
/** 13.1.3.2: Variance conversion. */
private predicate convVariance(VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
private predicate convVariance(ConstructedType fromType, ConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
@@ -637,95 +737,205 @@ private predicate convVariance(VarianceConvertibleConstructedType fromType, Vari
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
*/
exists(UnboundGenericType ugt |
convVarianceFromZero(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
/**
* Holds if the type arguments 0 through `i` of types `fromType` and `toType`
* are variance convertible.
*/
private predicate convVarianceFromZero(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
exists(Type toTypeArgument |
convVarianceFromZeroAux(ugt, fromType, i, toTypeArgument) |
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
i = 0 and
fromType != toType
)
Variance::convVarianceSingle(_, fromType, toType)
or
exists(Type toTypeArgument |
convVarianceFromZero(ugt, fromType, toType, i - 1) |
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
convVarianceFromZeroAux(ugt, fromType, i, toTypeArgument)
exists(UnboundGenericType ugt |
Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
pragma [nomagic]
private predicate convVarianceFromZeroAux(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, int i, Type toTypeArgument) {
exists(Type fromTypeArgument, TypeParameter tp |
fromTypeArgument = getTypeArgument(ugt, fromType, i, tp) |
convIdentity(fromTypeArgument, toTypeArgument)
private module Variance {
/**
* Holds if constructed type `ct` is potentially variance convertible to
* or from another constructed type, as a result of the `i`th type
* argument being potentially convertible.
*/
private predicate isVarianceConvertible(ConstructedType ct, int i) {
exists(TypeParameter tp, Type t |
tp = ct.getUnboundGeneric().getTypeParameter(i) and
t = ct.getTypeArgument(i) |
(
// Anything that is not a type parameter is potentially convertible
// to/from another type; if the `i`th type parameter is invariant,
// `t` must be strictly identity convertible
not t instanceof TypeParameter
and
(tp.isIn() or tp.isOut() or Identity::convIdentityStrict(t, _))
)
or
exists(TypeParameter s |
s = t |
// A type parameter with implicit reference conversion
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
or
// A type parameter convertible from another type parameter
exists(TypeParameter u | s = convTypeParameterBase(u) and u.isRefType() and tp.isIn())
)
)
}
private class VarianceConvertibleConstructedType extends ConstructedType {
VarianceConvertibleConstructedType() {
isVarianceConvertible(this, _)
}
}
/**
* Gets the number of different type arguments supplied for the type
* parameter at index `i` in unbound generic type `ugt`.
*/
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
result = strictcount(Type arg |
exists(VarianceConvertibleConstructedType ct |
ct.getUnboundGeneric() = ugt |
arg = ct.getTypeArgument(i)
)
)
}
private int rnk(UnboundGenericType ugt, int i) {
result = rank[i + 1](int j, int k |
j = getTypeArgumentCount(ugt, k) |
k order by j, k
)
}
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
private Type getTypeArgumentRanked(UnboundGenericType ugt, VarianceConvertibleConstructedType t, int i, TypeParameter tp) {
result = getTypeArgument(ugt, t, rnk(ugt, i), tp)
}
pragma[noinline]
private Type getATypeArgumentRankedOut(int i) {
result = getTypeArgumentRanked(_, _, i, any(TypeParameter tp | tp.isOut()))
}
pragma[noinline]
private Type getATypeArgumentRankedIn(int i) {
result = getTypeArgumentRanked(_, _, i, any(TypeParameter tp | tp.isIn()))
}
private predicate convRefTypeTypeArgumentOut(TypeArgument fromType, TypeArgument toType, int i) {
convRefTypeNonNull(fromType, toType) and
toType = getATypeArgumentRankedOut(i)
}
private predicate convRefTypeTypeArgumentIn(TypeArgument toType, TypeArgument fromType, int i) {
convRefTypeNonNull(toType, fromType) and
toType = getATypeArgumentRankedIn(i)
}
private newtype TVariance = TNone() or TIn() or TOut()
/**
* Holds if `fromTypeArgument` is convertible to `toTypeArgument`, with variance
* `v`, and both types are the `i`th type argument in _some_ constructed type.
*/
pragma[nomagic]
private predicate convTypeArguments(TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v) {
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i, _) and
toTypeArgument = getTypeArgumentRanked(_, _, j, _) and
i <= j and j <= i
|
convIdentity(fromTypeArgument, toTypeArgument) and
v = TNone()
or
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, j) and
v = TOut()
or
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, j) and
v = TIn()
)
}
pragma[nomagic]
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
exists(TypeParameter tp, TVariance v |
convTypeArguments(fromTypeArgument, toTypeArgument, i, v) |
fromTypeArgument = getTypeArgumentRanked(ugt, _, i, tp) and
(v = TIn() implies tp.isIn()) and
(v = TOut() implies tp.isOut())
)
}
/**
* Holds if `fromTypeArgument` is convertible to `toTypeArgument` and
* both types are the `i`th type argument in _some_ constructed type
* based on unbound generic type `ugt`.
*/
pragma[noinline]
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, _, i, _)
}
pragma[nomagic]
private predicate convVarianceSingle0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0, _) and
ugt.getNumberOfTypeParameters() = 1
}
/**
* Holds if the type arguments of types `fromType` and `toType` are variance
* convertible, and the number of type arguments is 1.
*/
predicate convVarianceSingle(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convVarianceSingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0, _)
)
}
pragma[nomagic]
private predicate convVarianceMultiple01Aux0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0, _) and
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1, _)
}
pragma[nomagic]
private predicate convVarianceMultiple01Aux1(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0, _) and
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1, _)
}
/**
* Holds if the first two ranked type arguments of types `fromType` and `toType`
* are variance convertible.
*/
private predicate convVarianceMultiple01(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1 |
convVarianceMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
convVarianceMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
)
}
pragma[nomagic]
private predicate convVarianceMultiple2Aux(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, i, _) and
i >= 2
}
private predicate convVarianceMultiple2(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convVarianceMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i, _)
)
}
/**
* Holds if the ranked type arguments 0 through `i` (with `i >= 1`) of types
* `fromType` and `toType` are variance convertible.
*/
pragma[nomagic]
predicate convVarianceMultiple(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
convVarianceMultiple01(ugt, fromType, toType) and i = 1
or
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, i) and
tp.isOut()
or
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, i) and
tp.isIn()
)
}
pragma [nomagic]
private predicate convRefTypeTypeArgumentOut(TypeArgument fromType, TypeArgument toType, int i) {
exists(TypeParameter tp |
convRefTypeNonNull(fromType, toType) |
toType = getTypeArgument(_, _, i, tp) and
tp.isOut()
)
}
pragma [nomagic]
private predicate convRefTypeTypeArgumentIn(TypeArgument toType, TypeArgument fromType, int i) {
exists(TypeParameter tp |
convRefTypeNonNull(toType, fromType) |
toType = getTypeArgument(_, _, i, tp) and
tp.isIn()
)
}
private class VarianceConvertibleConstructedType extends ConstructedType {
VarianceConvertibleConstructedType() {
isVarianceConvertible(this, _)
convVarianceMultiple(ugt, fromType, toType, i - 1) and
convVarianceMultiple2(ugt, fromType, toType, i)
}
}
/**
* Holds if constructed type `ct` is potentially variance convertible to
* or from another constructed type, as a result of the `i`th type
* argument being potentially convertible.
*/
private predicate isVarianceConvertible(ConstructedType ct, int i) {
exists(TypeParameter tp, Type t |
tp = ct.getUnboundGeneric().getTypeParameter(i)
and
t = ct.getTypeArgument(i)
|
(
// Anything that is not a type parameter is potentially convertible
// to/from another type; if the `i`th type parameter is invariant,
// `t` must be strictly identity convertible
not t instanceof TypeParameter
and
(tp.isIn() or tp.isOut() or convIdentityStrict(t, _))
)
or
exists(TypeParameter s |
s = t |
// A type parameter with implicit reference conversion
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
or
// A type parameter convertible from another type parameter
exists(TypeParameter u | s = convTypeParameterBase(u) and u.isRefType() and tp.isIn())
)
)
}

View File

@@ -1,5 +1,5 @@
/**
* A library for working with XML files and their content.
* Provides classes and predicates for working with XML files and their content.
*/
import semmle.code.csharp.Element
@@ -18,7 +18,7 @@ class XMLLocatable extends @xmllocatable {
not this instanceof File // in the dbscheme
}
/** The source location for this element. */
/** Gets the source location for this element. */
Location getALocation() { xmllocations(this,result) }
/**
@@ -32,7 +32,7 @@ class XMLLocatable extends @xmllocatable {
)
}
/** A printable representation of this element. */
/** Gets a printable representation of this element. */
abstract string toString();
}
@@ -42,46 +42,49 @@ class XMLLocatable extends @xmllocatable {
*/
class XMLParent extends @xmlparent {
/**
* A printable representation of this XML parent.
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)
*/
abstract string getName();
/** The file to which this XML parent belongs. */
/** Gets the file to which this XML parent belongs. */
XMLFile getFile() { result = this or xmlElements(this,_,_,_,result) }
/** The child element at a specified index of this XML parent. */
/** Gets the child element at a specified index of this XML parent. */
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
/** A child element of this XML parent. */
/** Gets a child element of this XML parent. */
XMLElement getAChild() { xmlElements(result,_,this,_,_) }
/** A child element of this XML parent with the given `name`. */
/** Gets a child element of this XML parent with the given `name`. */
XMLElement getAChild(string name) { xmlElements(result,_,this,_,_) and result.hasName(name) }
/** A comment that is a child of this XML parent. */
/** Gets a comment that is a child of this XML parent. */
XMLComment getAComment() { xmlComments(result,_,this,_) }
/** A character sequence that is a child of this XML parent. */
/** Gets a character sequence that is a child of this XML parent. */
XMLCharacters getACharactersSet() { xmlChars(result,_,this,_,_,_) }
/** The depth in the tree. (Overridden in XMLElement.) */
/** Gets the depth in the tree. (Overridden in XMLElement.) */
int getDepth() { result = 0 }
/** The number of child XML elements of this XML parent. */
/** Gets the number of child XML elements of this XML parent. */
int getNumberOfChildren() {
result = count(XMLElement e | xmlElements(e,_,this,_,_))
}
/** The number of places in the body of this XML parent where text occurs. */
/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() {
result = count(int pos | xmlChars(_,_,this,pos,_,_))
}
/**
* DEPRECATED: Internal.
*
* Append the character sequences of this XML parent from left to right, separated by a space,
* up to a specified (zero-based) index.
*/
deprecated
string charsSetUpTo(int n) {
(n = 0 and xmlChars(_,result,this,0,_,_)) or
(n > 0 and exists(string chars | xmlChars(_,chars,this,n,_,_) |
@@ -90,18 +93,15 @@ class XMLParent extends @xmlparent {
/** Append all the character sequences of this XML parent from left to right, separated by a space. */
string allCharactersString() {
exists(int n | n = this.getNumberOfCharacterSets() |
(n = 0 and result = "") or
(n > 0 and result = this.charsSetUpTo(n-1))
)
result = concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
}
/** The text value contained in this XML parent. */
/** Gets the text value contained in this XML parent. */
string getTextValue() {
result = allCharactersString()
}
/** A printable representation of this XML parent. */
/** Gets a printable representation of this XML parent. */
string toString() { result = this.getName() }
}
@@ -111,51 +111,52 @@ class XMLFile extends XMLParent, File {
xmlEncoding(this,_)
}
/** A printable representation of this XML file. */
/** Gets a printable representation of this XML file. */
override string toString() { result = XMLParent.super.toString() }
/** The name of this XML file. */
/** Gets the name of this XML file. */
override string getName() { files(this,result,_,_,_) }
/** The path of this XML file. */
/** Gets the path of this XML file. */
string getPath() { files(this,_,result,_,_) }
/** The path of the folder that contains this XML file. */
/** Gets the path of the folder that contains this XML file. */
string getFolder() {
result = this.getPath().substring(0, this.getPath().length()-this.getName().length())
}
/** The encoding of this XML file. */
/** Gets the encoding of this XML file. */
string getEncoding() { xmlEncoding(this,result) }
/** The XML file itself. */
/** Gets the XML file itself. */
override
XMLFile getFile() { result = this }
/** A top-most element in an XML file. */
/** Gets a top-most element in an XML file. */
XMLElement getARootElement() { result = this.getAChild() }
/** A DTD associated with this XML file. */
/** Gets a DTD associated with this XML file. */
XMLDTD getADTD() { xmlDTDs(result,_,_,_,this) }
}
/** A "Document Type Definition" of an XML file. */
class XMLDTD extends XMLLocatable, @xmldtd {
/** The name of the root element of this DTD. */
/** Gets the name of the root element of this DTD. */
string getRoot() { xmlDTDs(this,result,_,_,_) }
/** The public ID of this DTD. */
/** Gets the public ID of this DTD. */
string getPublicId() { xmlDTDs(this,_,result,_,_) }
/** The system ID of this DTD. */
/** Gets the system ID of this DTD. */
string getSystemId() { xmlDTDs(this,_,_,result,_) }
/** Whether this DTD is public. */
/** Holds if this DTD is public. */
predicate isPublic() { not xmlDTDs(this,_,"",_,_) }
/** The parent of this DTD. */
/** Gets the parent of this DTD. */
XMLParent getParent() { xmlDTDs(this,_,_,_,result) }
/** Gets a printable representation of this DTD. */
override string toString() {
(this.isPublic() and result = this.getRoot() + " PUBLIC '" +
this.getPublicId() + "' '" +
@@ -168,89 +169,87 @@ class XMLDTD extends XMLLocatable, @xmldtd {
/** An XML tag in an XML file. */
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
/** Whether this XML element has the given `name`. */
/** Holds if this XML element has the given `name`. */
predicate hasName(string name) { name = getName() }
/** The name of this XML element. */
override
/** Gets the name of this XML element. */
override string getName() { xmlElements(this,result,_,_,_) }
/** The XML file in which this XML element occurs. */
/** Gets the XML file in which this XML element occurs. */
override
XMLFile getFile() { xmlElements(this,_,_,_,result) }
/** The parent of this XML element. */
/** Gets the parent of this XML element. */
XMLParent getParent() { xmlElements(this,_,result,_,_) }
/** The index of this XML element among its parent's children. */
/** Gets the index of this XML element among its parent's children. */
int getIndex() { xmlElements(this, _, _, result, _) }
/** Whether this XML element has a namespace. */
/** Holds if this XML element has a namespace. */
predicate hasNamespace() { xmlHasNs(this,_,_) }
/** The namespace of this XML element, if any. */
/** Gets the namespace of this XML element, if any. */
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
/** The index of this XML element among its parent's children. */
/** Gets the index of this XML element among its parent's children. */
int getElementPositionIndex() { xmlElements(this,_,_,result,_) }
/** The depth of this element within the XML file tree structure. */
/** Gets the depth of this element within the XML file tree structure. */
override
int getDepth() { result = this.getParent().getDepth() + 1 }
/** An XML attribute of this XML element. */
/** Gets an XML attribute of this XML element. */
XMLAttribute getAnAttribute() { result.getElement() = this }
/** The attribute with the specified `name`, if any. */
/** Gets the attribute with the specified `name`, if any. */
XMLAttribute getAttribute(string name) {
result.getElement() = this and result.getName() = name
}
/** Whether this XML element has an attribute with the specified `name`. */
/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) {
exists(XMLAttribute a| a = this.getAttribute(name))
}
/** The value of the attribute with the specified `name`, if any. */
/** Gets the value of the attribute with the specified `name`, if any. */
string getAttributeValue(string name) {
result = this.getAttribute(name).getValue()
}
/** A printable representation of this XML element. */
override
/** Gets a printable representation of this XML element. */
override string toString() { result = XMLParent.super.toString() }
}
/** An attribute that occurs inside an XML element. */
class XMLAttribute extends @xmlattribute, XMLLocatable {
/** The name of this attribute. */
/** Gets the name of this attribute. */
string getName() { xmlAttrs(this,_,result,_,_,_) }
/** The XML element to which this attribute belongs. */
/** Gets the XML element to which this attribute belongs. */
XMLElement getElement() { xmlAttrs(this,result,_,_,_,_) }
/** Whether this attribute has a namespace. */
/** Holds if this attribute has a namespace. */
predicate hasNamespace() { xmlHasNs(this,_,_) }
/** The namespace of this attribute, if any. */
/** Gets the namespace of this attribute, if any. */
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
/** The value of this attribute. */
/** Gets the value of this attribute. */
string getValue() { xmlAttrs(this,_,_,result,_,_) }
/** A printable representation of this XML attribute. */
/** Gets a printable representation of this XML attribute. */
override string toString() { result = this.getName() + "=" + this.getValue() }
}
/** A namespace used in an XML file */
class XMLNamespace extends XMLLocatable, @xmlnamespace {
/** The prefix of this namespace. */
/** Gets the prefix of this namespace. */
string getPrefix() { xmlNs(this,result,_,_) }
/** The URI of this namespace. */
/** Gets the URI of this namespace. */
string getURI() { xmlNs(this,_,result,_) }
/** Whether this namespace has no prefix. */
/** Holds if this namespace has no prefix. */
predicate isDefault() { this.getPrefix() = "" }
override string toString() {
@@ -261,13 +260,13 @@ class XMLNamespace extends XMLLocatable, @xmlnamespace {
/** A comment of the form `<!-- ... -->` is an XML comment. */
class XMLComment extends @xmlcomment, XMLLocatable {
/** The text content of this XML comment. */
/** Gets the text content of this XML comment. */
string getText() { xmlComments(this,result,_,_) }
/** The parent of this XML comment. */
/** Gets the parent of this XML comment. */
XMLParent getParent() { xmlComments(this,_,result,_) }
/** A printable representation of this XML comment. */
/** Gets a printable representation of this XML comment. */
override string toString() { result = this.getText() }
}
@@ -276,15 +275,15 @@ class XMLComment extends @xmlcomment, XMLLocatable {
* closing tags of an XML element, excluding other elements.
*/
class XMLCharacters extends @xmlcharacters, XMLLocatable {
/** The content of this character sequence. */
/** Gets the content of this character sequence. */
string getCharacters() { xmlChars(this,result,_,_,_,_) }
/** The parent of this character sequence. */
/** Gets the parent of this character sequence. */
XMLParent getParent() { xmlChars(this,_,result,_,_,_) }
/** Whether this character sequence is CDATA. */
/** Holds if this character sequence is CDATA. */
predicate isCDATA() { xmlChars(this,_,_,_,1,_) }
/** A printable representation of this XML character sequence. */
/** Gets a printable representation of this XML character sequence. */
override string toString() { result = this.getCharacters() }
}

View File

@@ -1 +0,0 @@
Files/FLinesOfCommentedCode.ql

View File

@@ -1 +0,0 @@
Files/FLinesOfDuplicatedCode.ql

View File

@@ -0,0 +1 @@
Metrics/Files/FLinesOfCommentedCode.ql

Some files were not shown because too many files have changed in this diff Show More