Merge branch 'master' into users/raulga/c6277

This commit is contained in:
Raul Garcia
2018-10-12 15:59:54 -07:00
committed by GitHub
1257 changed files with 62905 additions and 26218 deletions

View File

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

View File

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

View File

@@ -50,5 +50,6 @@ predicate unsignedGEZero(UnsignedGEZero ugez, string msg) {
ugez.getLocation().getStartLine() = mi.getLocation().getStartLine() and
ugez.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
) and
not ugez.isFromTemplateInstantiation(_) and
msg = "Pointless comparison of unsigned value to zero."
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,13 +4,13 @@
<qhelp>
<overview>
<p>This query indicates that an <code>HRESULT</code> is being cast to a boolean type or vice versa.</p>
<p>The typical success value (<code>S_OK</code>) of an <code>HRESULT</code> equals 0. However, 0 indicates failure for a boolean type.</p>
<p>Casting an <code>HRESULT</code> to a boolean type and then using it in a test expression will yield an incorrect result.</p>
<p>This query indicates that an <code>HRESULT</code> is being cast to a Boolean type or vice versa.</p>
<p>The typical success value (<code>S_OK</code>) of an <code>HRESULT</code> equals 0. However, 0 indicates failure for a Boolean type.</p>
<p>Casting an <code>HRESULT</code> to a Boolean type and then using it in a test expression will yield an incorrect result.</p>
</overview>
<recommendation>
<p>To check if a call that returns an HRESULT succeeded use the <code>FAILED</code> macro.</p>
<p>To check if a call that returns an <code>HRESULT</code> succeeded use the <code>FAILED</code> macro.</p>
</recommendation>
<example>

View File

@@ -1,8 +1,6 @@
/**
* @name Cast between semantically different integer types: HRESULT to/from a Boolean type
* @description Cast between semantically different integer types: HRESULT to/from a Boolean type.
* Boolean types indicate success by a non-zero value, whereas success (S_OK) in HRESULT is indicated by a value of 0.
* Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result.
* @name Cast between HRESULT and a Boolean type
* @description Casting an HRESULT to/from a Boolean type and then using it in a test expression will yield an incorrect result because success (S_OK) in HRESULT is indicated by a value of 0.
* @kind problem
* @id cpp/hresult-boolean-conversion
* @problem.severity error
@@ -68,4 +66,4 @@ where exists
)
and not isHresultBooleanConverted(e1)
)
select e1, msg
select e1, msg

View File

@@ -13,18 +13,19 @@
import cpp
import IncorrectPointerScalingCommon
private predicate isCharPtrExpr(Expr e) {
private predicate isCharSzPtrExpr(Expr e) {
exists (PointerType pt
| pt = e.getFullyConverted().getUnderlyingType()
| pt.getBaseType().getUnspecifiedType() instanceof CharType)
| pt.getBaseType().getUnspecifiedType() instanceof CharType
or pt.getBaseType().getUnspecifiedType() instanceof VoidType)
}
from Expr sizeofExpr, Expr e
where
// If we see an addWithSizeof then we expect the type of
// the pointer expression to be char*. Otherwise it is probably
// a mistake.
addWithSizeof(e, sizeofExpr, _) and not isCharPtrExpr(e)
// the pointer expression to be char* or void*. Otherwise it
// is probably a mistake.
addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e)
select
sizeofExpr,
"Suspicious sizeof offset in a pointer arithmetic expression. " +

View File

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

View File

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

View File

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

View File

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

View File

@@ -102,7 +102,8 @@ private predicate constructorCallStartLoc(ConstructorCall cc, File f, int line,
/**
* Holds if `f`, `line`, `column` indicate the start character
* of `tm`, which mentions `t`.
* of `tm`, which mentions `t`. Type mentions for instantiations
* are filtered out.
*/
private predicate typeMentionStartLoc(TypeMention tm, Type t, File f, int line, int column) {
exists(Location l |
@@ -111,7 +112,8 @@ private predicate typeMentionStartLoc(TypeMention tm, Type t, File f, int line,
l.getStartLine() = line and
l.getStartColumn() = column
) and
t = tm.getMentionedType()
t = tm.getMentionedType() and
not t instanceof ClassTemplateInstantiation
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import cpp
import semmle.code.cpp.dataflow.DataFlow
/**
* Holds if `sizeof(s)` occurs as part of the parameter of a dynamic
@@ -47,6 +48,7 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
/**
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
*/
language[monotonicAggregates]
int getBufferSize(Expr bufferExpr, Element why) {
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
(
@@ -58,6 +60,10 @@ int getBufferSize(Expr bufferExpr, Element why) {
// buffer is an initialized array
// e.g. int buffer[] = {1, 2, 3};
why = bufferVar.getInitializer().getExpr() and
(
why instanceof AggregateLiteral or
why instanceof StringLiteral
) and
result = why.(Expr).getType().(ArrayType).getSize() and
not exists(bufferVar.getType().getUnspecifiedType().(ArrayType).getSize())
) or exists(Class parentClass, VariableAccess parentPtr |
@@ -71,19 +77,25 @@ int getBufferSize(Expr bufferExpr, Element why) {
bufferVar.getType().getSize() -
parentClass.getSize()
)
) or exists(Expr def |
// buffer is assigned with an allocation
definitionUsePair(_, def, bufferExpr) and
exprDefinition(_, def, why) and
isFixedSizeAllocationExpr(why, result)
) or exists(Expr def, Expr e, Element why2 |
// buffer is assigned with another buffer
definitionUsePair(_, def, bufferExpr) and
exprDefinition(_, def, e) and
result = getBufferSize(e, why2) and
(
) or (
// buffer is a fixed size dynamic allocation
isFixedSizeAllocationExpr(bufferExpr, result) and
why = bufferExpr
) or (
// dataflow (all sources must be the same size)
result = min(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), DataFlow::exprNode(bufferExpr)) |
getBufferSize(def, _)
) and result = max(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), DataFlow::exprNode(bufferExpr)) |
getBufferSize(def, _)
) and
// find reason
exists(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), DataFlow::exprNode(bufferExpr)) |
why = def or
why = why2
exists(getBufferSize(def, why))
)
) or exists(Type bufferType |
// buffer is the address of a variable

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
import DataFlowUtil
private import DataFlowPrivate
private import DataFlowDispatch
@@ -55,9 +54,7 @@ private module ImplCommon {
*/
cached
predicate parameterValueFlowsThrough(ParameterNode p) {
exists(ReturnNode ret |
parameterValueFlow(p, ret)
)
exists(ReturnNode ret | parameterValueFlow(p, ret))
}
/**
@@ -112,8 +109,10 @@ private module ImplCommon {
storeViaSideEffect(node1, f, node2) or
storeReturn(node1, f, node2)
}
private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
storeStep(node1, f, node2) and readStep(_, f, _) or
storeStep(node1, f, node2) and readStep(_, f, _)
or
exists(Call call, int i1, int i2 |
setterCall(call, i1, i2, f) and
node1.(ArgumentNode).argumentOf(call, i1) and
@@ -122,6 +121,7 @@ private module ImplCommon {
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
)
}
pragma[nomagic]
private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
exists(Node n1, PostUpdateNode n2 |
@@ -131,6 +131,7 @@ private module ImplCommon {
p1 != p2
)
}
pragma[nomagic]
private predicate setterCall(Call call, int i1, int i2, Content f) {
exists(Callable callable, ParameterNode p1, ParameterNode p2 |
@@ -140,6 +141,7 @@ private module ImplCommon {
p2.isParameterOf(callable, i2)
)
}
private predicate storeReturn(Node node1, Content f, Node node2) {
exists(ParameterNode p, ArgumentNode arg |
arg = node1 and
@@ -150,6 +152,7 @@ private module ImplCommon {
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
)
}
private predicate setterReturn(ParameterNode p, Content f) {
exists(Node n1, Node n2, ReturnNode ret |
parameterValueFlow(p, n1) and
@@ -164,7 +167,8 @@ private module ImplCommon {
*/
cached
predicate read(Node node1, Content f, Node node2) {
readStep(node1, f, node2) and storeStep(_, f, _) or
readStep(node1, f, node2) and storeStep(_, f, _)
or
exists(ParameterNode p, ArgumentNode arg |
arg = node1 and
viableParamArg(p, arg) and
@@ -174,6 +178,7 @@ private module ImplCommon {
compatibleTypes(node2.getTypeBound(), f.getType())
)
}
private predicate getter(ParameterNode p, Content f) {
exists(Node n1, Node n2, ReturnNode ret |
parameterValueFlow(p, n1) and
@@ -196,9 +201,7 @@ private module ImplCommon {
* expression that reaches a `this` parameter.
*/
private predicate callHasInstanceArgument(Call call) {
exists(ArgumentNode arg |
arg.argumentOf(call, -1)
)
exists(ArgumentNode arg | arg.argumentOf(call, -1))
}
cached
@@ -207,9 +210,11 @@ private module ImplCommon {
TSpecificCall(Call call, int i, boolean emptyAp) {
reducedViableImplInCallContext(_, _, call) and
(emptyAp = true or emptyAp = false) and
(exists(call.getArgument(i))
or
i = -1 and callHasInstanceArgument(call))
(
exists(call.getArgument(i))
or
i = -1 and callHasInstanceArgument(call)
)
} or
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
TReturn(Method m, MethodAccess ma) { reducedViableImplInReturn(m, ma) }
@@ -232,19 +237,22 @@ import ImplCommon
* this dispatch target of `ma` implies a reduced set of dispatch origins
* to which data may flow if it should reach a `return` statement.
*/
abstract class CallContext extends TCallContext {
abstract string toString();
}
abstract class CallContext extends TCallContext { abstract string toString(); }
class CallContextAny extends CallContext, TAnyCallContext {
override string toString() { result = "CcAny" }
}
abstract class CallContextCall extends CallContext { }
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() { result = "CcCall" }
}
class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
}
class CallContextReturn extends CallContext, TReturn {
override string toString() { result = "CcReturn" }
}
@@ -263,12 +271,14 @@ predicate resolveReturn(CallContext cc, Callable callable, Call call) {
bindingset[call, cc]
Callable resolveCall(Call call, CallContext cc) {
exists(Call ctx | cc = TSpecificCall(ctx, _, _) |
if reducedViableImplInCallContext(call, _, ctx) then
result = prunedViableImplInCallContext(call, ctx)
else
result = viableCallable(call)
) or
result = viableCallable(call) and cc instanceof CallContextSomeCall or
result = viableCallable(call) and cc instanceof CallContextAny or
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
else result = viableCallable(call)
)
or
result = viableCallable(call) and cc instanceof CallContextSomeCall
or
result = viableCallable(call) and cc instanceof CallContextAny
or
result = viableCallable(call) and cc instanceof CallContextReturn
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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