mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge branch 'master' into hresult-boolean-qhelp
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.cpp
Normal file
3
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
wchar_t* pSrc;
|
||||
|
||||
pSrc = (wchar_t*)"a"; // casting a byte-string literal "a" to a wide-character string
|
||||
35
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.qhelp
Normal file
35
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.qhelp
Normal 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>
|
||||
29
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
Normal file
29
cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
Normal 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."
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
485
cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
Normal file
485
cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
Normal 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()
|
||||
}
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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(_)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" } }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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:
|
||||
* ```
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user