mirror of
https://github.com/github/codeql.git
synced 2026-07-20 10:48:17 +02:00
Merge remote-tracking branch 'upstream/master' into mergeback-20181130
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
|
||||
* Arguments passed using `in` are now extracted.
|
||||
* Fix a bug where the `dynamic` type name was not extracted correctly in certain circumstances.
|
||||
* Fixed a bug where method type signatures were extracted incorrectly in some circumstances.
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
|
||||
19
change-notes/1.20/analysis-csharp.md
Normal file
19
change-notes/1.20/analysis-csharp.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* |
|
||||
| Off-by-one comparison against container length (cs/index-out-of-bounds) | Fewer false positives | Results have been removed when there are additional guards on the index. |
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
## Changes to the autobuilder
|
||||
@@ -1,199 +0,0 @@
|
||||
|
||||
import cpp
|
||||
import CPython.Extensions
|
||||
|
||||
/* A call to an argument parsing function */
|
||||
class PyArgParseTupleCall extends FunctionCall {
|
||||
|
||||
PyArgParseTupleCall() {
|
||||
this.getTarget().hasGlobalName("PyArg_Parse") or
|
||||
this.getTarget().hasGlobalName("PyArg_ParseTuple") or
|
||||
this.getTarget().hasGlobalName("PyArg_VaParse") or
|
||||
this.getTarget().hasGlobalName("PyArg_ParseTupleAndKeywords") or
|
||||
this.getTarget().hasGlobalName("PyArg_VaParseAndKeywords")
|
||||
}
|
||||
|
||||
private int getFormatIndex() {
|
||||
exists(Function f | f = this.getTarget() |
|
||||
(f.hasGlobalName("PyArg_Parse") or f.hasGlobalName("PyArg_ParseTuple") or f.hasGlobalName("PyArg_VaParse")) and result = 1
|
||||
or
|
||||
(f.hasGlobalName("PyArg_ParseTupleAndKeywords") or f.hasGlobalName("PyArg_VaParseAndKeywords")) and result = 2
|
||||
)
|
||||
}
|
||||
|
||||
private string getFormatString() {
|
||||
result = this.getArgument(this.getFormatIndex()).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
string getArgumentFormat() {
|
||||
exists(string fmt | fmt = this.getFormatString() |
|
||||
exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":" | result = fmt.prefix(i))
|
||||
or
|
||||
not exists(int i | fmt.charAt(i) = ";" or fmt.charAt(i) = ":") and result = fmt
|
||||
)
|
||||
}
|
||||
|
||||
string getPyArgumentType(int index) {
|
||||
parse_format_string(this.getArgumentFormat(), index, _, result) and result != "typed"
|
||||
or
|
||||
exists(int cindex, PythonClass cls | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") |
|
||||
cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand() and
|
||||
result = cls.getTpName()
|
||||
)
|
||||
or
|
||||
exists(int cindex | parse_format_string(this.getArgumentFormat(), index, cindex, "typed") and
|
||||
not exists(PythonClass cls | cls.getAnAccess() = this.getArgument(this.getFormatIndex() * 2 + cindex).(AddressOfExpr).getOperand())
|
||||
and result = "object"
|
||||
)
|
||||
}
|
||||
|
||||
predicate pyArgumentIsOptional(int index) {
|
||||
exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) |
|
||||
suffix.charAt(0) = "|")
|
||||
}
|
||||
|
||||
predicate pyArgumentIsKwOnly(int index) {
|
||||
exists(string suffix | split_format_string(this.getArgumentFormat(), _, _, suffix, index, _) |
|
||||
suffix.charAt(0) = "$")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PyUnpackTupleCall extends FunctionCall {
|
||||
|
||||
PyUnpackTupleCall() {
|
||||
this.getTarget().hasGlobalName("PyArg_UnpackTuple")
|
||||
}
|
||||
|
||||
int getMinSize() {
|
||||
result = this.getArgument(2).getValue().toInt()
|
||||
}
|
||||
|
||||
int getMaxSize() {
|
||||
result = this.getArgument(3).getValue().toInt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate limiting_format(string text, string limit) {
|
||||
text = "t#" and limit = "read-only"
|
||||
or
|
||||
(text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and limit = "non-negative"
|
||||
or
|
||||
(text = "c" or text = "C") and limit = "length-one"
|
||||
}
|
||||
|
||||
predicate format_string(string text, string type, int cargs) {
|
||||
tuple_format(text, type, cargs) or simple_format(text, type, cargs)
|
||||
}
|
||||
|
||||
private
|
||||
predicate simple_format(string text, string type, int cargs) {
|
||||
text = "s" and (type = "str" or type = "unicode") and cargs = 1
|
||||
or
|
||||
text = "s#" and (type = "str" or type = "unicode") and cargs = 2
|
||||
or
|
||||
text = "s*" and (type = "str" or type = "unicode") and cargs = 1
|
||||
or
|
||||
text = "z" and (type = "str" or type = "unicode" or type = "NoneType") and cargs = 1
|
||||
or
|
||||
text = "z#" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 2
|
||||
or
|
||||
text = "z*" and (type = "str" or type = "unicode" or type = "NoneType" or type = "buffer") and cargs = 1
|
||||
or
|
||||
text = "u" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "u#" and type = "unicode" and cargs = 2
|
||||
or
|
||||
text = "O" and type = "object" and cargs = 1
|
||||
or
|
||||
text = "p" and type = "object" and cargs = 1
|
||||
or
|
||||
text = "O&" and type = "object" and cargs = 2
|
||||
or
|
||||
text = "O!" and type = "typed" and cargs = 2
|
||||
or
|
||||
(text = "b" or text = "h" or text = "i" or text = "l" or text = "L" or text = "n") and type = "int" and cargs = 1
|
||||
or
|
||||
(text = "B" or text = "H" or text = "I" or text = "k" or text = "K") and type = "int" and cargs = 1
|
||||
or
|
||||
text = "c" and (type = "bytes" or type = "bytearray") and cargs = 1
|
||||
or
|
||||
text = "C" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "D" and type = "complex" and cargs = 1
|
||||
or
|
||||
(text = "f" or text = "d") and type = "float" and cargs = 1
|
||||
or
|
||||
text = "S" and type = "str" and cargs = 1
|
||||
or
|
||||
text = "U" and type = "unicode" and cargs = 1
|
||||
or
|
||||
text = "t#" and type = "buffer" and cargs = 2
|
||||
or
|
||||
text = "w" and type = "buffer" and cargs = 1
|
||||
or
|
||||
text = "w#" and type = "buffer" and cargs = 2
|
||||
or
|
||||
text = "w*" and type = "buffer" and cargs = 1
|
||||
or
|
||||
(text = "es" or text = "et") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 2
|
||||
or
|
||||
(text = "es#" or text = "et#") and (type = "str" or type = "unicode" or type = "buffer") and cargs = 3
|
||||
or
|
||||
text = "y" and type = "bytes" and cargs = 1
|
||||
or
|
||||
text = "y*" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 1
|
||||
or
|
||||
text = "y#" and (type = "bytes" or type = "bytearray" or type = "buffer") and cargs = 2
|
||||
}
|
||||
|
||||
private
|
||||
predicate tuple_format(string text, string type, int cargs) {
|
||||
type = "tuple" and
|
||||
exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(text)))
|
||||
and
|
||||
exists(string body | text = "(" + body + ")" | tuple_body(body, _, cargs))
|
||||
}
|
||||
|
||||
private
|
||||
predicate tuple_body(string body, int pyargs, int cargs) {
|
||||
body = "" and cargs = 0 and pyargs = 0
|
||||
or
|
||||
(exists(PyArgParseTupleCall call | exists(call.getArgumentFormat().indexOf(body))) and
|
||||
exists(string p, int pargs, string s, int sargs, int pyargsm1 | pyargs = pyargsm1+1 and tuple_body(p, pyargsm1, pargs) and
|
||||
format_string(s, _, sargs) and body = p + s and cargs = pargs + sargs)
|
||||
)
|
||||
}
|
||||
|
||||
predicate format_token(string token, int delta, int cdelta) {
|
||||
format_string(token, _, cdelta) and delta = 1
|
||||
or
|
||||
token = "|" and delta = 0 and cdelta = 0
|
||||
or
|
||||
token = "$" and delta = 0 and cdelta = 0
|
||||
}
|
||||
|
||||
predicate split_format_string(string full, string prefix, string text, string suffix, int index, int cindex) {
|
||||
exists(PyArgParseTupleCall call | call.getArgumentFormat() = full) and
|
||||
full = prefix + text + suffix and
|
||||
(suffix = "" or exists(string s | suffix.prefix(s.length()) = s | format_token(s, _, _))) and
|
||||
format_token(text, _, _) and
|
||||
(prefix = "" and index = 0 and cindex = 0 and suffix = full.suffix(text.length())
|
||||
or
|
||||
exists(string prefixm1, string suffixm1, string textm1, int im1, int cim1, int prev, int cprev |
|
||||
full = prefixm1 + textm1 + suffixm1 and
|
||||
split_format_string(full, prefixm1, textm1, suffixm1, im1, cim1) and
|
||||
format_token(textm1, prev, cprev) and
|
||||
index = im1+prev and
|
||||
cindex = cim1+cprev and
|
||||
prefix = prefixm1 + textm1 and
|
||||
suffix = suffixm1.suffix(text.length()) and
|
||||
text = suffixm1.prefix(text.length())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate parse_format_string(string full, int index, int cindex, string type) {
|
||||
exists(string prefix, string text, string suffix | split_format_string(full, prefix, text, suffix, index, cindex) and format_string(text, type, _))
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @name Parameter type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension function parameter types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/argument-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
from TypedPythonExtensionFunction func, int arg, PythonClass cls
|
||||
where func.getArgumentType(arg) = cls
|
||||
select "ext_argtype", func.getTrapID(), arg, cls.getTrapID()
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* @name py_cobject_sources() trap file generator
|
||||
* @description Generate trap files (in CSV form) for CPython objects.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/c-object-sources-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
from CObject c
|
||||
select "py_cobject_sources", c.getTrapID(), 1
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @name py_cobject() trap file generator
|
||||
* @description Generate trap files (in CSV form) for CPython objects.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/c-object-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from CObject c
|
||||
select "py_cobjects", c.getTrapID()
|
||||
@@ -1,893 +0,0 @@
|
||||
import cpp
|
||||
import CPython.ArgParse
|
||||
|
||||
|
||||
/* Root class of all 'C' objects */
|
||||
abstract class CObject extends Element {
|
||||
|
||||
abstract string getTrapID();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A Python class is an instance of PyTypeObject.
|
||||
*/
|
||||
class PythonClass extends Variable, CObject {
|
||||
|
||||
PythonClass() {
|
||||
getType().hasName("PyTypeObject")
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_methods")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the getset table (if any) associated with the class. */
|
||||
PythonGetSetTable getGetSetTable() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_getset")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the Python module (if any) containing this class. */
|
||||
PythonModule getModule() {
|
||||
result = getFile()
|
||||
}
|
||||
|
||||
Expr getSlot(string name) {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName(name)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = result
|
||||
)
|
||||
}
|
||||
|
||||
string getTpName() {
|
||||
exists(StringLiteral lit |
|
||||
lit = this.getSlot("tp_name") |
|
||||
result = lit.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
Expr getSizeOf() {
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = getInitializer().getExpr()
|
||||
and tt.hasName("PyTypeObject")
|
||||
and f.hasName("tp_basicsize")
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = result
|
||||
)
|
||||
}
|
||||
|
||||
override string getTrapID() {
|
||||
/* This needs to be kept in sync with extractor-python/semmle/passes/type.py */
|
||||
result = "C_type$" + this.getTpName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A call to a Py_InitModule function. These functions register a Python module.
|
||||
*/
|
||||
class Py_InitModuleCall extends FunctionCall {
|
||||
Py_InitModuleCall() {
|
||||
// Py_InitModule itself is actually a macro, ultimately defined to be something like Py_InitModule4_64.
|
||||
getTarget().getName().matches("Py\\_Init%")
|
||||
}
|
||||
|
||||
/** Gets the name of the module being registered. */
|
||||
string getModuleName() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
/** Gets the function table associated with this Py_InitModule call. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(VariableAccess va |
|
||||
va = getArgument(1)
|
||||
and result = va.getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A Python module, represented by the file containing an initialising call for it.
|
||||
*/
|
||||
class PythonModule extends File {
|
||||
PythonModule() {
|
||||
exists(PythonModuleDefinition def | def.getFile() = this)
|
||||
or
|
||||
exists(FunctionCall c | c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a Python class that is in this module. */
|
||||
PythonClass getAClass() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/** Gets the function table associated with the module. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
result = this.getDefinition().getFunctionTable()
|
||||
or
|
||||
exists(FunctionCall c | c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%") and
|
||||
c.getAnArgument() = result.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the Py_InitModule call that was used to register the module. */
|
||||
//private
|
||||
PythonModuleDefinition getDefinition() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/** Gets the name of the module. */
|
||||
string getModuleName() {
|
||||
result = this.getDefinition().getModuleName()
|
||||
or
|
||||
exists(FunctionCall c |c.getFile() = this |
|
||||
c.getTarget().getName().matches("Py\\_InitModule%") and
|
||||
c.getArgument(0).getValue() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The function table for a Python module.
|
||||
*/
|
||||
class PythonFunctionTable extends Variable {
|
||||
|
||||
PythonFunctionTable() {
|
||||
not(this instanceof Parameter)
|
||||
and exists(ArrayType at | at = getType().getUnspecifiedType() and at.getBaseType().hasName("PyMethodDef"))
|
||||
}
|
||||
|
||||
/** Gets an entry in the table. */
|
||||
PythonFunctionTableEntry getATableEntry() {
|
||||
result = getInitializer().getExpr().getAChild()
|
||||
and exists(result.getRegisteredFunctionName())
|
||||
}
|
||||
|
||||
/** Gets the class (if any) for which this is the function table. */
|
||||
PythonClass getClass() {
|
||||
result.getFunctionTable() = this
|
||||
or
|
||||
exists(FunctionAccess getattr, Call find_method |
|
||||
result.getSlot("tp_getattr") = getattr |
|
||||
find_method.getEnclosingFunction() = getattr.getTarget() and
|
||||
find_method.getArgument(0) = this.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the module (if any) for which this is the function table. */
|
||||
PythonModule getModule() {
|
||||
result.getFunctionTable() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The getset table for a Python module or type
|
||||
*/
|
||||
class PythonGetSetTable extends Variable {
|
||||
|
||||
PythonGetSetTable() {
|
||||
not(this instanceof Parameter) and
|
||||
exists(ArrayType at | at = getType() and at.getBaseType().hasName("PyGetSetDef"))
|
||||
}
|
||||
|
||||
/** Gets the class (if any) for which this is the function table. */
|
||||
PythonClass getClass() {
|
||||
result.getGetSetTable() = this
|
||||
}
|
||||
|
||||
/** Gets an entry in the table. */
|
||||
PythonGetSetTableEntry getATableEntry() {
|
||||
result = getInitializer().getExpr().getAChild()
|
||||
and exists(result.getRegisteredPropertyName())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PythonModuleDefinition extends Variable {
|
||||
|
||||
PythonModuleDefinition() {
|
||||
not(this instanceof Parameter)
|
||||
and exists(Type moddef_t | moddef_t = this.getType() and moddef_t.hasName("PyModuleDef"))
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
PythonFunctionTable getFunctionTable() {
|
||||
exists(ClassAggregateLiteral l, Type moddef_t, Field f |
|
||||
l = this.getInitializer().getExpr()
|
||||
and moddef_t.hasName("PyModuleDef")
|
||||
and f.hasName("m_methods")
|
||||
and f.getDeclaringType() = moddef_t
|
||||
and result.getAnAccess() = l.getFieldExpr(f)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the function table (if any) associated with the class. */
|
||||
string getModuleName() {
|
||||
exists(ClassAggregateLiteral l, Type moddef_t, Field f |
|
||||
l = this.getInitializer().getExpr()
|
||||
and moddef_t.hasName("PyModuleDef")
|
||||
and f.hasName("m_name")
|
||||
and f.getDeclaringType() = moddef_t
|
||||
and result = l.getFieldExpr(f).getValue()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A special (__xxx__) method implemented in C
|
||||
*/
|
||||
class PythonSpecialMethod extends Function {
|
||||
|
||||
PythonSpecialMethod() {
|
||||
class_special_methods(_, _, this)
|
||||
}
|
||||
|
||||
PythonClass getClass() {
|
||||
class_special_methods(result, _, this)
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
class_special_methods(_, result, this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate class_special_methods(PythonClass cls, string name, Function method) {
|
||||
|
||||
exists(string slot, FunctionAccess fa |
|
||||
special_methods(name, slot) and cls.getSlot(slot) = fa and fa.getTarget() = method
|
||||
or
|
||||
number_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_number")
|
||||
and tt.hasName("PyNumberMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
or
|
||||
sequence_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_sequence")
|
||||
and tt.hasName("PySequenceMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
or
|
||||
mapping_methods(name, slot) and
|
||||
exists(ClassAggregateLiteral l, TypedefType tt, Field f |
|
||||
l = cls.getSlot("tp_as_mapping")
|
||||
and tt.hasName("PyMappingMethods")
|
||||
and f.hasName(slot)
|
||||
and f.getDeclaringType() = tt.getBaseType()
|
||||
and l.getFieldExpr(f) = fa
|
||||
and fa.getTarget() = method
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
predicate special_methods(string name, string slot_name) {
|
||||
name = "__getattr__" and slot_name = "tp_getattr"
|
||||
or
|
||||
name = "__hash__" and slot_name = "tp_hash"
|
||||
or
|
||||
name = "__call__" and slot_name = "tp_call"
|
||||
or
|
||||
name = "__str__" and slot_name = "tp_str"
|
||||
or
|
||||
name = "__getattribute__" and slot_name = "tp_getattro"
|
||||
or
|
||||
name = "__setattro__" and slot_name = "tp_setattro"
|
||||
or
|
||||
name = "__iter__" and slot_name = "tp_iter"
|
||||
or
|
||||
name = "__descr_get__" and slot_name = "tp_descr_get"
|
||||
or
|
||||
name = "__descr_set__" and slot_name = "tp_descr_set"
|
||||
}
|
||||
|
||||
predicate number_methods(string name, string slot_name) {
|
||||
name = "__add__" and slot_name = "nb_add"
|
||||
or
|
||||
name = "__sub__" and slot_name = "nb_subtract"
|
||||
or
|
||||
name = "__mul__" and slot_name = "nb_multiply"
|
||||
or
|
||||
name = "__mod__" and slot_name = "nb_remainder"
|
||||
or
|
||||
name = "__pow__" and slot_name = "nb_power"
|
||||
or
|
||||
name = "__neg__" and slot_name = "nb_negative"
|
||||
or
|
||||
name = "__pos__" and slot_name = "nb_positive"
|
||||
or
|
||||
name = "__abs__" and slot_name = "nb_absolute"
|
||||
or
|
||||
name = "__bool__" and slot_name = "nb_bool"
|
||||
or
|
||||
name = "__bool__" and slot_name = "nb_bool"
|
||||
or
|
||||
name = "__invert__" and slot_name = "nb_invert"
|
||||
or
|
||||
name = "__lshift__" and slot_name = "nb_lshift"
|
||||
or
|
||||
name = "__rshift__" and slot_name = "nb_rshift"
|
||||
or
|
||||
name = "__and__" and slot_name = "nb_and"
|
||||
or
|
||||
name = "__xor__" and slot_name = "nb_xor"
|
||||
or
|
||||
name = "__or__" and slot_name = "nb_or"
|
||||
or
|
||||
name = "__int__" and slot_name = "nb_int"
|
||||
or
|
||||
name = "__long__" and slot_name = "nb_long"
|
||||
or
|
||||
name = "__float__" and slot_name = "nb_float"
|
||||
or
|
||||
name = "__iadd__" and slot_name = "nb_inplace_add"
|
||||
or
|
||||
name = "__isub__" and slot_name = "nb_inplace_subtract"
|
||||
or
|
||||
name = "__imul__" and slot_name = "nb_inplace_multiply"
|
||||
or
|
||||
name = "__imod__" and slot_name = "nb_inplace_remainder"
|
||||
or
|
||||
name = "__ilshift__" and slot_name = "nb_inplace_lshift"
|
||||
or
|
||||
name = "__irshift__" and slot_name = "nb_inplace_rshift"
|
||||
or
|
||||
name = "__iand__" and slot_name = "nb_inplace_and"
|
||||
or
|
||||
name = "__ixor__" and slot_name = "nb_inplace_xor"
|
||||
or
|
||||
name = "__ior__" and slot_name = "nb_inplace_or"
|
||||
or
|
||||
name = "__index__" and slot_name = "nb_index"
|
||||
}
|
||||
|
||||
predicate sequence_methods(string name, string slot_name) {
|
||||
name = "__len__" and slot_name = "sq_length"
|
||||
or
|
||||
name = "__add__" and slot_name = "sq_concat"
|
||||
or
|
||||
name = "__mul__" and slot_name = "sq_repeat"
|
||||
or
|
||||
name = "__getitem__" and slot_name = "sq_item"
|
||||
or
|
||||
name = "__setitem__" and slot_name = "sq_ass_item"
|
||||
or
|
||||
name = "__contains__" and slot_name = "sq_contains"
|
||||
or
|
||||
name = "__iadd__" and slot_name = "sq_inplace_concat"
|
||||
or
|
||||
name = "__imul__" and slot_name = "sq_inplace_repeat"
|
||||
}
|
||||
|
||||
predicate mapping_methods(string name, string slot_name) {
|
||||
name = "__len__" and slot_name = "mp_length"
|
||||
or
|
||||
name = "__getitem__" and slot_name = "mp_subscript"
|
||||
or
|
||||
name = "__setitem__" and slot_name = "mp_ass_subscript"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
An entry in the getset table for a Python class.
|
||||
This is the C code item that corresponds 1-to-1 with the Python-level property
|
||||
*/
|
||||
class PythonGetSetTableEntry extends AggregateLiteral {
|
||||
|
||||
PythonGetSetTableEntry() {
|
||||
this.getUnderlyingType().hasName("PyGetSetDef")
|
||||
and
|
||||
this.getChild(0) instanceof StringLiteral
|
||||
}
|
||||
|
||||
Function getGetter() {
|
||||
exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
Function getSetter() {
|
||||
exists(FunctionAccess fa | fa = getChild(2) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
StringLiteral getRegisteredPropertyName() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
|
||||
PythonGetSetTable getTable() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
An entry in the function table for a Python class or module.
|
||||
This is the C code item that corresponds 1-to-1 with the Python-level function.
|
||||
*/
|
||||
class PythonFunctionTableEntry extends AggregateLiteral {
|
||||
|
||||
PythonFunctionTableEntry() {
|
||||
this.getUnderlyingType().hasName("PyMethodDef")
|
||||
and
|
||||
this.getChild(0) instanceof StringLiteral
|
||||
}
|
||||
|
||||
/** Gets the doc string to be associated with the function being registered. */
|
||||
string getDocString() {
|
||||
result = getChild(3).(StringLiteral).getValueText()
|
||||
}
|
||||
|
||||
/** Gets the flags for the function being registered. */
|
||||
int getFlags() {
|
||||
result = getChild(2).getValue().toInt()
|
||||
}
|
||||
|
||||
/** Gets the function being registered. */
|
||||
Function getFunction() {
|
||||
exists(FunctionAccess fa | fa = getChild(1) and result = fa.getTarget())
|
||||
}
|
||||
|
||||
/** Gets the module containing the function table. */
|
||||
PythonModule getModule() {
|
||||
result = getTable().getModule()
|
||||
}
|
||||
|
||||
/** Gets the name with which the function should be referenced from Python. */
|
||||
StringLiteral getRegisteredFunctionName() {
|
||||
result = this.getChild(0)
|
||||
}
|
||||
|
||||
/** Gets the function table containing this entry. */
|
||||
PythonFunctionTable getTable() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
|
||||
/** Gets a flag associated with this function. */
|
||||
string getAFlag() {
|
||||
exists(int f | f = this.getFlags() |
|
||||
(f % 2 = 1 and result = "METH_VARARGS")
|
||||
or ((f / 2) % 2 = 1 and result = "METH_KEYWORDS")
|
||||
or ((f / 4) % 2 = 1 and result = "METH_NOARGS")
|
||||
or ((f / 8) % 2 = 1 and result = "METH_O")
|
||||
or ((f / 16) % 2 = 1 and result = "METH_CLASS")
|
||||
or ((f / 32) % 2 = 1 and result = "METH_STATIC")
|
||||
or ((f / 64) % 2 = 1 and result = "METH_COEXIST")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
library class PythonBuildReturnCall extends FunctionCall {
|
||||
PythonBuildReturnCall() {
|
||||
exists(string name | name = getTarget().getName() |
|
||||
name = "Py_BuildValue"
|
||||
or name = "Py_VaBuildValue"
|
||||
)
|
||||
}
|
||||
|
||||
string getFormatString() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
An extension function for Python (written in C).
|
||||
*/
|
||||
class PythonExtensionFunction extends Function {
|
||||
PythonExtensionFunction() {
|
||||
exists(PythonFunctionTableEntry e | e.getFunction() = this)
|
||||
and exists(getAParameter())
|
||||
}
|
||||
|
||||
/** Gets a function table entry registering this function. */
|
||||
PythonFunctionTableEntry getATableEntry() {
|
||||
result.getFunction() = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TypedPythonExtensionProperty extends PythonGetSetTableEntry, CObject {
|
||||
PythonClass getPropertyType() {
|
||||
result = py_return_type(this.getGetter())
|
||||
}
|
||||
|
||||
private string trapClass() {
|
||||
result = this.getClass().getTrapID()
|
||||
}
|
||||
|
||||
override string getTrapID() {
|
||||
result = this.trapClass() + "$" + this.getPyName()
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
result = this.getRegisteredPropertyName().getValue()
|
||||
}
|
||||
|
||||
/** Gets the class containing this function. */
|
||||
PythonClass getClass() {
|
||||
result = this.getTable().getClass()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* An extension function for Python (written in C); Python facing aspect */
|
||||
abstract class TypedPythonExtensionFunction extends PythonFunctionTableEntry, CObject {
|
||||
|
||||
override Location getLocation() {
|
||||
result = this.getRegisteredFunctionName().getLocation()
|
||||
}
|
||||
|
||||
override
|
||||
string toString() {
|
||||
result = "MethodDef " + this.getRegisteredFunctionName().getValue()
|
||||
}
|
||||
|
||||
abstract PythonClass getArgumentType(int index);
|
||||
|
||||
abstract predicate argumentIsOptional(int index);
|
||||
|
||||
abstract predicate argumentIsKwOnly(int index);
|
||||
|
||||
PythonExtensionFunction getCode() {
|
||||
result.getATableEntry() = this
|
||||
}
|
||||
|
||||
predicate isMethod() {
|
||||
exists(this.getTable().getClass()) and not this.getAFlag() = "METH_STATIC"
|
||||
}
|
||||
|
||||
int c_index(int index) {
|
||||
index in [0..20] and result in [-1..20]
|
||||
and
|
||||
(if this.isMethod() then
|
||||
result = index - 1
|
||||
else
|
||||
result = index
|
||||
)
|
||||
}
|
||||
|
||||
string getPyName() {
|
||||
result = this.getRegisteredFunctionName().getValue()
|
||||
}
|
||||
|
||||
PythonClass getReturnType() {
|
||||
result = py_return_type(this.getCode())
|
||||
}
|
||||
|
||||
|
||||
/** Gets the module containing this function. */
|
||||
override PythonModule getModule() {
|
||||
result = getTable().getModule()
|
||||
}
|
||||
|
||||
/** Gets the class containing this function. */
|
||||
PythonClass getClass() {
|
||||
result = getTable().getClass()
|
||||
}
|
||||
|
||||
//private
|
||||
string trapModule() {
|
||||
result = this.getModule().getModuleName()
|
||||
}
|
||||
|
||||
//private
|
||||
string trapClass() {
|
||||
result = this.getClass().getTrapID()
|
||||
}
|
||||
|
||||
/* A globally unique name for use in trap files.
|
||||
*/
|
||||
override string getTrapID() {
|
||||
result = "C_builtin_function_or_method$" + this.trapModule() + "." + this.getPyName()
|
||||
or
|
||||
result = this.trapClass() + "$" + this.getPyName()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
predicate src_dest_pair(Element src, ControlFlowNode dest) {
|
||||
exists(LocalScopeVariable v, ControlFlowNode def |
|
||||
definitionUsePair(v, def, dest) and
|
||||
exprDefinition(v, def, src) and
|
||||
not exists(AddressOfExpr addr | addr.getOperand() = v.getAnAccess())
|
||||
)
|
||||
or
|
||||
exists(Parameter p | dest = p.getAnAccess() and not definitionUsePair(_, _, dest) and src = p)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate local_flows_to(Element src, ControlFlowNode dest) {
|
||||
not unreachable(src) and not unreachable(dest) and
|
||||
(src = dest
|
||||
or
|
||||
src_dest_pair(src, dest)
|
||||
or
|
||||
exists(Element mid | local_flows_to(src, mid) and src_dest_pair(mid, dest))
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass py_return_type(Function f) {
|
||||
exists(ReturnStmt ret |
|
||||
ret.getEnclosingFunction() = f and
|
||||
result = python_type(ret.getExpr()) and
|
||||
not ret.getExpr().getValue() = "0"
|
||||
)
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_NONE" and result.getTpName() = "NoneType")
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_TRUE" and result.getTpName() = "bool")
|
||||
or
|
||||
exists(Macro m | m.getAnInvocation().getEnclosingFunction() = f and m.getName() = "Py_RETURN_FALSE" and result.getTpName() = "bool")
|
||||
}
|
||||
|
||||
PythonClass python_type_from_size(Expr e) {
|
||||
exists(Type t, string name |
|
||||
t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() and name.matches("Py\\_%Object") |
|
||||
exists(PythonClass cls | cls.getSizeOf().getValueText() = "sizeof(" + t.getName() + ")" |
|
||||
result = cls and not result.getTpName() = "int" and not result.getTpName() = "bool"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate py_bool(Expr e) {
|
||||
exists(MacroInvocation mi, string name |
|
||||
mi.getExpr() = e and name = mi.getMacroName() |
|
||||
name = "Py_False" or name = "Py_True"
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass python_type_from_name(Expr e) {
|
||||
exists(Type t, string name |
|
||||
t = e.getUnderlyingType().(PointerType).getBaseType() and name = t.getName() |
|
||||
name = "PyBytesObject" and result.getTpName() = "bytes"
|
||||
or
|
||||
name = "PyTupleObject" and result.getTpName() = "tuple"
|
||||
or
|
||||
name = "PyLongObject" and result.getTpName() = "int" and not py_bool(e)
|
||||
or
|
||||
name = "PyIntObject" and result.getTpName() = "int" and not py_bool(e)
|
||||
or
|
||||
name = "PyStringObject" and result.getTpName() = "str" and cpython_major_version() = 2
|
||||
or
|
||||
name = "PyStringObject" and result.getTpName() = "bytes" and cpython_major_version() = 3
|
||||
or
|
||||
name = "PyUnicodeObject" and result.getTpName() = "unicode" and cpython_major_version() = 2
|
||||
or
|
||||
name = "PyUnicodeObject" and result.getTpName() = "str" and cpython_major_version() = 3
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass python_type(Expr e) {
|
||||
result = python_type_from_size(e)
|
||||
or
|
||||
result = python_type_from_name(e)
|
||||
or
|
||||
call_to_new(e, result)
|
||||
or
|
||||
exists(Element src | result = python_type(src) and local_flows_to(src, e))
|
||||
or
|
||||
result = type_from_build_value(e)
|
||||
or
|
||||
result = type_from_call(e)
|
||||
or
|
||||
py_bool(e) and result.getTpName() = "bool"
|
||||
or
|
||||
call_to_type(e, result)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
mi.getExpr() = e and mi.getMacroName() = "Py_None" |
|
||||
result.getTpName() = "NoneType"
|
||||
)
|
||||
}
|
||||
|
||||
predicate build_value_function(Function f) {
|
||||
f.getName().regexpMatch("_?Py_(Va)?BuildValue(_SizeT)?")
|
||||
}
|
||||
|
||||
PythonClass type_from_build_value(Expr e) {
|
||||
exists(FunctionCall c |
|
||||
c = e |
|
||||
build_value_function(c.getTarget()) and
|
||||
result = type_from_build_value_code(c.getArgument(0).getValue())
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass type_from_call(Expr e) {
|
||||
exists(Function f |
|
||||
not build_value_function(f) and
|
||||
/* Do not type to infer return type of the interpreter */
|
||||
not f.getName().matches("PyEval%") and
|
||||
f = e.(FunctionCall).getTarget() and
|
||||
result = py_return_type(f)
|
||||
)
|
||||
or
|
||||
exists(Function f |
|
||||
f = e.(FunctionCall).getTarget() and
|
||||
result.getTpName() = "dict"
|
||||
|
|
||||
f.hasName("PyEval_GetBuiltins") or
|
||||
f.hasName("PyEval_GetGlobals") or
|
||||
f.hasName("PyEval_GetLocals")
|
||||
)
|
||||
}
|
||||
|
||||
PythonClass type_from_build_value_code(string s) {
|
||||
exists(FunctionCall c | c.getArgument(0).getValue() = s)
|
||||
and
|
||||
(result = type_from_simple_code(s)
|
||||
or
|
||||
result.getTpName() = "dict" and s.charAt(0) = "{"
|
||||
or
|
||||
result.getTpName() = "tuple" and not exists(type_from_simple_code(s)) and not s.charAt(0) = "{"
|
||||
)
|
||||
}
|
||||
|
||||
private PythonClass theBytesClass() {
|
||||
cpython_major_version() = 2 and result.getTpName() = "str"
|
||||
or
|
||||
cpython_major_version() = 3 and result.getTpName() = "bytes"
|
||||
}
|
||||
|
||||
private PythonClass theUnicodeClass() {
|
||||
cpython_major_version() = 2 and result.getTpName() = "unicode"
|
||||
or
|
||||
cpython_major_version() = 3 and result.getTpName() = "str"
|
||||
}
|
||||
|
||||
PythonClass type_from_simple_code(string s) {
|
||||
(s = "s" or s = "s#" or s = "z" or s = "z#") and result.getTpName() = "str"
|
||||
or
|
||||
(s = "u" or s = "u#" or s = "U" or s = "U#" or s = "C") and result = theUnicodeClass()
|
||||
or
|
||||
(s = "y" or s = "y#" or s = "c") and result = theBytesClass()
|
||||
or
|
||||
(s = "i" or s = "I" or s = "b" or s = "B" or s = "h" or s = "H" or
|
||||
s = "k" or s = "K" or s = "l" or s = "L" or s = "n"
|
||||
) and result.getTpName() = "int"
|
||||
or
|
||||
(s = "f" or s = "d") and result.getTpName() = "float"
|
||||
or
|
||||
s = "D" and result.getTpName() = "complex"
|
||||
or
|
||||
(s = "O" or s = "O&" or s = "N") and result.getTpName() = "object"
|
||||
}
|
||||
|
||||
predicate call_to_new(FunctionCall call, PythonClass cls) {
|
||||
exists(string name |
|
||||
name = call.getTarget().getName() |
|
||||
name = "_PyObject_New" or
|
||||
name = "_PyObject_GC_New" or
|
||||
name = "_PyObject_NewVar" or
|
||||
name = "_PyObject_GC_NewVar"
|
||||
) and
|
||||
exists(AddressOfExpr addr |
|
||||
addr = call.getArgument(0) |
|
||||
addr.getAddressable() = cls
|
||||
)
|
||||
}
|
||||
|
||||
predicate call_to_type(FunctionCall call, PythonClass cls) {
|
||||
exists(AddressOfExpr aoe |
|
||||
call.getTarget().getName().matches("PyObject\\_Call%") and
|
||||
call.getArgument(0) = aoe and
|
||||
aoe.getAddressable() = cls
|
||||
)
|
||||
}
|
||||
|
||||
predicate pyargs_function(PythonFunctionTableEntry func, PyArgParseTupleCall call) {
|
||||
func.getFunction().getParameter(1).getAnAccess() = call.getArgument(0)
|
||||
}
|
||||
|
||||
|
||||
class PyArgsFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyArgsFunction() {
|
||||
this.getAFlag() = "METH_VARARGS"
|
||||
}
|
||||
|
||||
PyArgParseTupleCall getParseArgsCall() {
|
||||
strictcount(PyArgParseTupleCall other | this.getCode().getParameter(1).getAnAccess() = other.getArgument(0)) = 1 and
|
||||
pyargs_function(this, result)
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
or
|
||||
result.getTpName() = this.getParseArgsCall().getPyArgumentType(this.c_index(index))
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
this.getParseArgsCall().pyArgumentIsOptional(this.c_index(index))
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
this.getParseArgsCall().pyArgumentIsKwOnly(this.c_index(index))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PyOFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyOFunction() {
|
||||
this.getAFlag() = "METH_O"
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
or
|
||||
/* TO DO -- Better analysis */
|
||||
this.c_index(index) = 0 and result.getTpName() = "object"
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class PyNoArgFunction extends TypedPythonExtensionFunction {
|
||||
|
||||
PyNoArgFunction() {
|
||||
this.getAFlag() = "METH_NOARGS"
|
||||
}
|
||||
|
||||
override PythonClass getArgumentType(int index) {
|
||||
this.isMethod() and index = 0 and result = this.getTable().getClass()
|
||||
}
|
||||
|
||||
override predicate argumentIsOptional(int index) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate argumentIsKwOnly(int index) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
int cpython_major_version() {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "PY_MAJOR_VERSION" |
|
||||
result = mi.getExpr().getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
int cpython_minor_version() {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "PY_MINOR_VERSION" |
|
||||
result = mi.getExpr().getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
string cpython_version() {
|
||||
result = cpython_major_version().toString() + "." + cpython_minor_version().toString()
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Parameter return trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension functions return one of their parameters.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/parameter-return-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import CPython.Extensions
|
||||
|
||||
predicate argument_flows_to_return(Function func, int arg) {
|
||||
exists(Parameter p | p = func.getParameter(arg) |
|
||||
forall(ReturnStmt ret | ret.getEnclosingFunction() = func |
|
||||
local_flows_to(p, ret.getExpr()))
|
||||
)
|
||||
}
|
||||
|
||||
from TypedPythonExtensionFunction func, PythonExtensionFunction code, int arg
|
||||
where func.getCode() = code and argument_flows_to_return(code, arg)
|
||||
select "ext_argreturn", func.getTrapID(), arg
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @name Property type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension property types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/property-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from TypedPythonExtensionProperty p, PythonClass cls
|
||||
where cls = p.getPropertyType()
|
||||
select "ext_proptype", p.getTrapID(), cls.getTrapID()
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @name Return type trap file generator
|
||||
* @description Generate trap files (in CSV form) describing CPython extension function return types.
|
||||
* @kind trap
|
||||
* @id cpp/c-python/return-type-trap
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
import CPython.Extensions
|
||||
|
||||
|
||||
from TypedPythonExtensionFunction func, PythonClass cls
|
||||
where cls = func.getReturnType()
|
||||
select "ext_rettype", func.getTrapID(), cls.getTrapID()
|
||||
@@ -3,7 +3,11 @@
|
||||
* @description Query to help investigate mysterious results with ReturnStackAllocatedObject
|
||||
* @kind table
|
||||
* @id cpp/points-to/debug
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
// This query is not suitable for production use and has been deprecated.
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
* @description Query to force evaluation of staged points-to predicates
|
||||
* @kind table
|
||||
* @id cpp/points-to/prepared-staged-points-to
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
// This query is not suitable for production use and has been deprecated.
|
||||
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
select count(int set, Element location | setlocations(set, unresolveElement(location))),
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
* @description Count the number points-to sets with 0 or 1 incoming flow edges, and the total number of points-to sets
|
||||
* @kind table
|
||||
* @id cpp/points-to/stats
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
// This query is not suitable for production use and has been deprecated.
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
* @name Taint test
|
||||
* @kind table
|
||||
* @id cpp/points-to/tainted-format-strings
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
// This query is not suitable for production use and has been deprecated.
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
import semmle.code.cpp.pointsto.CallGraph
|
||||
|
||||
@@ -19,7 +19,10 @@ predicate hasPragmaDifferentFile(File f) {
|
||||
exists (PreprocessorLine pl, string s |
|
||||
pl.getFile() = f and
|
||||
pl.getHead().splitAt(" ", 1) = s and /* Zero index is line number, one index is file reference */
|
||||
not ("\"" + f.getAbsolutePath() + "\"" = s))
|
||||
not ("\"" + f.getAbsolutePath() + "\"" = s) and
|
||||
not ("\"" + f.getRelativePath() + "\"" = s) and
|
||||
not ("\"" + f.getBaseName() + "\"" = s)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,13 @@ predicate functionEntry(ControlFlowNode entry) {
|
||||
and not hasMultiScopeNode(function))
|
||||
}
|
||||
|
||||
/** Holds if `exit` is the exit node of a function. */
|
||||
predicate functionExit(ControlFlowNode exit) {
|
||||
exists (Function function |
|
||||
function = exit
|
||||
and not hasMultiScopeNode(function))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dest` is an immediate successor of `src` in the control-flow graph.
|
||||
*/
|
||||
@@ -35,12 +42,25 @@ private predicate nodeSucc(ControlFlowNode src, ControlFlowNode dest) {
|
||||
src.getASuccessor() = dest
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` is an immediate predecessor of `src` in the control-flow graph.
|
||||
*/
|
||||
private predicate nodePred(ControlFlowNode src, ControlFlowNode pred) {
|
||||
src.getAPredecessor() = pred
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is an immediate dominator of `node` in the control-flow
|
||||
* graph.
|
||||
*/
|
||||
predicate iDominates(ControlFlowNode dominator, ControlFlowNode node) = idominance(functionEntry/1,nodeSucc/2)(_, dominator, node)
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is an immediate post-dominator of `node` in the control-flow
|
||||
* graph.
|
||||
*/
|
||||
predicate iPostDominates(ControlFlowNode postDominator, ControlFlowNode node) = idominance(functionExit/1,nodePred/2)(_, postDominator, node)
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is a strict dominator of `node` in the control-flow
|
||||
* graph. Being strict means that `dominator != node`.
|
||||
@@ -49,6 +69,14 @@ predicate strictlyDominates(ControlFlowNode dominator, ControlFlowNode node) {
|
||||
iDominates+(dominator, node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is a strict post-dominator of `node` in the control-flow
|
||||
* graph. Being strict means that `postDominator != node`.
|
||||
*/
|
||||
predicate strictlyPostDominates(ControlFlowNode postDominator, ControlFlowNode node) {
|
||||
iPostDominates+(postDominator, node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is a dominator of `node` in the control-flow graph. This
|
||||
* is reflexive.
|
||||
@@ -57,6 +85,14 @@ predicate dominates(ControlFlowNode dominator, ControlFlowNode node) {
|
||||
strictlyDominates(dominator, node) or dominator = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is a post-dominator of `node` in the control-flow graph. This
|
||||
* is reflexive.
|
||||
*/
|
||||
predicate postDominates(ControlFlowNode postDominator, ControlFlowNode node) {
|
||||
strictlyPostDominates(postDominator, node) or postDominator = node
|
||||
}
|
||||
|
||||
/*
|
||||
* Dominance predicates for basic blocks.
|
||||
*/
|
||||
@@ -67,6 +103,25 @@ predicate dominates(ControlFlowNode dominator, ControlFlowNode node) {
|
||||
*/
|
||||
predicate bbIDominates(BasicBlock dom, BasicBlock node) = idominance(functionEntry/1, bb_successor/2)(_, dom, node)
|
||||
|
||||
/**
|
||||
* Holds if `pred` is a predecessor of `succ` in the control-flow graph of
|
||||
* basic blocks.
|
||||
*/
|
||||
private predicate bb_predecessor(BasicBlock succ, BasicBlock pred) {
|
||||
bb_successor(pred, succ)
|
||||
}
|
||||
|
||||
/** Holds if `exit` is an `ExitBasicBlock`. */
|
||||
private predicate bb_exit(ExitBasicBlock exit) {
|
||||
any()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is an immediate post-dominator of `node` in the control-flow
|
||||
* graph of basic blocks.
|
||||
*/
|
||||
predicate bbIPostDominates(BasicBlock pDom, BasicBlock node) = idominance(bb_exit/1, bb_predecessor/2)(_, pDom, node)
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is a strict dominator of `node` in the control-flow
|
||||
* graph of basic blocks. Being strict means that `dominator != node`.
|
||||
@@ -75,6 +130,13 @@ predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) {
|
||||
bbIDominates+(dominator, node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is a strict post-dominator of `node` in the control-flow
|
||||
* graph of basic blocks. Being strict means that `postDominator != node`.
|
||||
*/
|
||||
predicate bbStrictlyPostDominates(BasicBlock postDominator, BasicBlock node) {
|
||||
bbIPostDominates+(postDominator, node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is a dominator of `node` in the control-flow graph of
|
||||
@@ -83,3 +145,11 @@ predicate bbStrictlyDominates(BasicBlock dominator, BasicBlock node) {
|
||||
predicate bbDominates(BasicBlock dominator, BasicBlock node) {
|
||||
bbStrictlyDominates(dominator, node) or dominator = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `postDominator` is a post-dominator of `node` in the control-flow graph of
|
||||
* basic blocks. This is reflexive.
|
||||
*/
|
||||
predicate bbPostDominates(BasicBlock postDominator, BasicBlock node) {
|
||||
bbStrictlyPostDominates(postDominator, node) or postDominator = node
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ int lambdas(int captured) {
|
||||
auto f1 = [&] { captured++; }; // capture has location "file://:0:0:0:0"
|
||||
f1();
|
||||
auto f2 = [&captured] { captured++; };
|
||||
f1();
|
||||
f2();
|
||||
return captured;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:18:38:30 | ... + ... | addressOf.cpp:40:15:40:15 | i |
|
||||
| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:40:15:40:15 | i | addressOf.cpp:42:19:42:22 | iref |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:48:3:48:4 | f1 |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:50:3:50:4 | f1 |
|
||||
| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} | addressOf.cpp:50:3:50:4 | f2 |
|
||||
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | addressOf.cpp:57:19:57:19 | a |
|
||||
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:18:57:45 | ... + ... | addressOf.cpp:58:18:58:18 | a |
|
||||
| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | indirect_use.cpp:21:17:21:17 | p |
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
| addressOf.cpp:42:19:42:22 | iref | non-const address |
|
||||
| addressOf.cpp:48:3:48:4 | f1 | |
|
||||
| addressOf.cpp:49:15:49:22 | captured | non-const address |
|
||||
| addressOf.cpp:50:3:50:4 | f1 | |
|
||||
| addressOf.cpp:50:3:50:4 | f2 | |
|
||||
| addressOf.cpp:51:10:51:17 | captured | |
|
||||
| addressOf.cpp:56:16:56:16 | i | |
|
||||
| addressOf.cpp:56:19:56:19 | i | |
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
| addressOf.cpp:46:17:46:24 | captured | addressOf.cpp:51:10:51:17 | captured |
|
||||
| addressOf.cpp:46:17:46:24 | captured | file://:0:0:0:0 | captured |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:50:3:50:4 | f1 |
|
||||
| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:50:3:50:4 | f2 |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:24:56:24 | i |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:20:38:20 | i |
|
||||
| addressOf.cpp:46:17:46:24 | captured | addressOf.cpp:51:10:51:17 | captured |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:50:3:50:4 | f1 |
|
||||
| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:50:3:50:4 | f2 |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:24:56:24 | i |
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | addressOf.cpp:34:23:34:23 | i |
|
||||
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:18:34:18 | i | addressOf.cpp:35:23:35:23 | i |
|
||||
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:34:23:34:23 | i | addressOf.cpp:35:23:35:23 | i |
|
||||
| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:48:3:48:4 | f1 | addressOf.cpp:50:3:50:4 | f1 |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | addressOf.cpp:56:19:56:19 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:16:56:16 | i | addressOf.cpp:56:24:56:24 | i |
|
||||
| addressOf.cpp:55:17:55:17 | i | addressOf.cpp:56:19:56:19 | i | addressOf.cpp:56:24:56:24 | i |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename) | test.c:9:23:9:26 | argv | user input (argv) |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-022/TaintedPath.ql
|
||||
@@ -0,0 +1,13 @@
|
||||
// Semmle test case for rule TaintedPath.ql (User-controlled data in path expression)
|
||||
// Associated with CWE-022: Improper Limitation of a Pathname to a Restricted Directory. http://cwe.mitre.org/data/definitions/22.html
|
||||
|
||||
///// Library routines /////
|
||||
|
||||
typedef struct {} FILE;
|
||||
#define FILENAME_MAX 1000
|
||||
typedef unsigned long size_t;
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
size_t strlen(const char *s);
|
||||
char *strncat(char *s1, const char *s2, size_t n);
|
||||
@@ -0,0 +1,30 @@
|
||||
// Semmle test case for rule TaintedPath.ql (User-controlled data in path expression)
|
||||
// Associated with CWE-022: Improper Limitation of a Pathname to a Restricted Directory. http://cwe.mitre.org/data/definitions/22.html
|
||||
|
||||
#include "stdlib.h"
|
||||
|
||||
///// Test code /////
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char *userAndFile = argv[2];
|
||||
|
||||
{
|
||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
||||
char *fileName = fileBuffer;
|
||||
size_t len = strlen(fileName);
|
||||
strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);
|
||||
// BAD: a string from the user is used in a filename
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
{
|
||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
||||
char *fileName = fileBuffer;
|
||||
size_t len = strlen(fileName);
|
||||
// GOOD: use a fixed file
|
||||
char* fixed = "file.txt";
|
||||
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.c:21:12:21:19 | command1 | This argument to an OS command is derived from $@ and then passed to system(string) | test.c:14:20:14:23 | argv | user input (argv) |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-078/ExecTainted.ql
|
||||
@@ -0,0 +1,33 @@
|
||||
// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command)
|
||||
// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html
|
||||
|
||||
///// Library routines /////
|
||||
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
int system(const char *string);
|
||||
|
||||
extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
|
||||
|
||||
///// Test code /////
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char *userName = argv[2];
|
||||
|
||||
{
|
||||
// BAD: a string from the user is injected directly into
|
||||
// a command.
|
||||
char command1[1000] = {0};
|
||||
sprintf(command1, "userinfo -v \"%s\"", userName);
|
||||
system(command1);
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: the user string is encoded by a library routine.
|
||||
char userNameQuoted[1000] = {0};
|
||||
encodeShellString(userNameQuoted, 1000, userName);
|
||||
char command2[1000] = {0};
|
||||
sprintf(command2, "userinfo -v %s", userNameQuoted);
|
||||
system(command2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| search.c:17:8:17:12 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data |
|
||||
| search.c:23:39:23:43 | query | Cross-site scripting vulnerability due to $@. | search.c:41:21:41:26 | call to getenv | this query data |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-079/CgiXss.ql
|
||||
@@ -0,0 +1,50 @@
|
||||
// Semmle test case for rule CgiXss.ql (CGI script vulnerable to cross-site scripting)
|
||||
// Associated with CWE-079: Cross-site Scripting. http://cwe.mitre.org/data/definitions/79.html
|
||||
|
||||
// Standard library routines
|
||||
void free(void *ptr);
|
||||
char *getenv(const char *name);
|
||||
int printf(const char *format, ...);
|
||||
int puts(const char *s);
|
||||
|
||||
// External routines
|
||||
char* do_search(char* query);
|
||||
char* escape_html(char* raw);
|
||||
|
||||
void bad_server1(char* query) {
|
||||
puts("<p>Query results for ");
|
||||
// BAD: Printing out an HTTP parameter with no escaping
|
||||
puts(query);
|
||||
puts("\n<p>\n");
|
||||
puts(do_search(query));
|
||||
}
|
||||
|
||||
void bad_server2(char* query) {
|
||||
printf("<p>Query results for %s\n", query);
|
||||
// BAD: Printing out an HTTP parameter with no escaping
|
||||
puts("\n<p>");
|
||||
puts(do_search(query));
|
||||
}
|
||||
|
||||
void good_server(char* query) {
|
||||
puts("<p>Query results for ");
|
||||
// GOOD: Escape HTML characters before adding to a page
|
||||
char* query_escaped = escape_html(query);
|
||||
puts(query_escaped);
|
||||
free(query_escaped);
|
||||
|
||||
puts("\n<p>\n");
|
||||
puts(do_search(query));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char* raw_query = getenv("QUERY_STRING");
|
||||
if (strcmp("good", argv[0]) == 0) {
|
||||
good_server(raw_query);
|
||||
} else if (strcmp("bad1", argv[0]) == 0) {
|
||||
bad_server1(raw_query);
|
||||
} else {
|
||||
bad_server2(raw_query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv |
|
||||
| test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-114/UncontrolledProcessOperation.ql
|
||||
@@ -0,0 +1,44 @@
|
||||
// Semmle test cases for CWE-114
|
||||
|
||||
int system(const char *string);
|
||||
char *getenv(const char* name);
|
||||
|
||||
// ---
|
||||
|
||||
class MyBase
|
||||
{
|
||||
public:
|
||||
virtual void doCommand1(const char *command) = 0;
|
||||
virtual void doCommand2(const char *command) = 0;
|
||||
virtual void doCommand3(const char *command) = 0;
|
||||
};
|
||||
|
||||
class MyDerived : public MyBase
|
||||
{
|
||||
public:
|
||||
void doCommand1(const char *command)
|
||||
{
|
||||
system(command); // GOOD
|
||||
}
|
||||
|
||||
void doCommand2(const char *command)
|
||||
{
|
||||
system(command); // BAD (externally controlled string)
|
||||
}
|
||||
|
||||
void doCommand3(const char *command)
|
||||
{
|
||||
system(command); // BAD (externally controlled string)
|
||||
}
|
||||
};
|
||||
|
||||
void testMyDerived()
|
||||
{
|
||||
MyDerived *md1 = new MyDerived;
|
||||
MyDerived *md2 = new MyDerived;
|
||||
MyBase *md3 = new MyDerived; // MyBase pointer to a MyDerived
|
||||
|
||||
md1->doCommand1("fixed");
|
||||
md2->doCommand2(getenv("varname"));
|
||||
md3->doCommand3(getenv("varname"));
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
| test.c:28:19:28:20 | 41 | Potential buffer-overflow: 'buffer' has size 40 not 41. |
|
||||
| test.c:29:26:29:27 | 43 | Potential buffer-overflow: 'buffer' has size 40 not 43. |
|
||||
| test.c:31:26:31:27 | 44 | Potential buffer-overflow: 'buffer' has size 40 not 44. |
|
||||
| test.c:32:25:32:26 | 45 | Potential buffer-overflow: 'buffer' has size 40 not 45. |
|
||||
| test.c:33:26:33:27 | 46 | Potential buffer-overflow: 'buffer' has size 40 not 46. |
|
||||
| test.c:34:22:34:23 | 47 | Potential buffer-overflow: 'buffer' has size 40 not 47. |
|
||||
| test.c:35:23:35:24 | 48 | Potential buffer-overflow: 'buffer' has size 40 not 48. |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/OverflowStatic.ql
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Semmle test case for OverflowStatic.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
|
||||
typedef struct {} FILE;
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef void *va_list;
|
||||
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
char *fgets(char *s, int n, FILE *stream);
|
||||
char *strncpy(char *s1, const char *s2, size_t n);
|
||||
char *strncat(char *s1, const char *s2, size_t n);
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
void *memmove(void *s1, const void *s2, size_t n);
|
||||
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void bad0(char *src, FILE *f, va_list ap) {
|
||||
char buffer[40];
|
||||
|
||||
fgets(buffer, 41, f); // BAD: Too many characters read
|
||||
strncpy(buffer, src, 43); // BAD: Too many characters copied
|
||||
buffer[0] = 0;
|
||||
strncat(buffer, src, 44); // BAD: Too many characters copied
|
||||
memcpy(buffer, src, 45); // BAD: Too many characters copied
|
||||
memmove(buffer, src, 46); // BAD: Too many characters copied
|
||||
snprintf(buffer, 47, "%s", src); // BAD: Too many characters copied
|
||||
vsnprintf(buffer, 48, "%s", ap); // BAD: Too many characters copied
|
||||
}
|
||||
|
||||
void good0(char *src, FILE *f, va_list ap) {
|
||||
char buffer[60];
|
||||
fread(buffer, sizeof(char), 51, f); // GOOD
|
||||
fgets(buffer, 52, f); // GOOD
|
||||
strncpy(buffer, src, 53); // GOOD
|
||||
buffer[0] = 0;
|
||||
strncat(buffer, src, 54); // GOOD
|
||||
memcpy(buffer, src, 55); // GOOD
|
||||
memmove(buffer, src, 56); // GOOD
|
||||
snprintf(buffer, 57, "%s", src); // GOOD
|
||||
vsnprintf(buffer, 58, "%s", ap); // GOOD
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.c:22:2:22:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| test.c:33:2:33:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
@@ -0,0 +1,2 @@
|
||||
Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Semmle test case for StrncpyFlippedArgs.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
extern char *strncpy(char *dest, const char *src, unsigned int sz);
|
||||
extern unsigned int strlen(const char *s);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void good0(char *arg) {
|
||||
char buf[80];
|
||||
// GOOD: Checks size of destination
|
||||
strncpy(buf, arg, sizeof(buf));
|
||||
}
|
||||
|
||||
void bad0(char *arg) {
|
||||
char buf[80];
|
||||
// BAD: Checks size of source
|
||||
strncpy(buf, arg, strlen(arg));
|
||||
|
||||
}
|
||||
|
||||
void good1(const char *buf, char *arg) {
|
||||
// GOOD: Checks size of destination
|
||||
strncpy(buf, arg, sizeof(buf));
|
||||
}
|
||||
|
||||
void bad1(const char *buf, char *arg) {
|
||||
// BAD: Checks size of source
|
||||
strncpy(buf, arg, strlen(arg));
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.c:24:2:24:8 | call to strncat | Potentially unsafe call to strncat. |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Semmle test case for SuspiciousCallToStrncat.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
typedef unsigned long size_t;
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
char *strncat(char *s1, const char *s2, size_t n);
|
||||
size_t strlen(const char *s);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void good0(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s = ");
|
||||
strncat(buf, s, sizeof(buf)-5); // GOOD
|
||||
strncat(buf, ".", 1); // BAD [NOT DETECTED] -- there might not be even 1 character of space
|
||||
}
|
||||
|
||||
void bad0(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s = ");
|
||||
strncat(buf, s, sizeof(buf)); // BAD -- Forgot to allow for "s = "
|
||||
strncat(buf, ".", 1); // BAD [NOT DETECTED] -- there might not be even 1 character of space
|
||||
}
|
||||
|
||||
void good1(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s = ");
|
||||
strncat(buf, s, sizeof(buf)-strlen("s = ")); // GOOD
|
||||
strncat(buf, ".", sizeof(buf)-strlen("s = ")-strlen(s)); // GOOD
|
||||
}
|
||||
|
||||
void bad1(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s = ");
|
||||
strncat(buf, s, sizeof(buf)-strlen("s = ")); // GOOD
|
||||
strncat(buf, ".", 1); // BAD [NOT DETECTED] -- Need to check if any space is left
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'call to strncpy' operation is limited to 1025 bytes but the destination is only 1024 bytes. |
|
||||
| var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'call to strncpy' operation is limited to 129 bytes but the destination is only 128 bytes. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/BadlyBoundedWrite.ql
|
||||
@@ -0,0 +1 @@
|
||||
Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
|
||||
@@ -0,0 +1,69 @@
|
||||
| overflowdestination.cpp:46:2:46:7 | call to memcpy | This 'memcpy' operation accesses 128 bytes but the $@ is only 64 bytes. | overflowdestination.cpp:40:7:40:10 | dest | destination buffer |
|
||||
| tests.cpp:23:2:23:7 | call to memcpy | This 'memcpy' operation accesses 20 bytes but the $@ is only 10 bytes. | tests.cpp:19:7:19:17 | smallbuffer | source buffer |
|
||||
| tests.cpp:25:2:25:7 | call to memcpy | This 'memcpy' operation accesses 20 bytes but the $@ is only 10 bytes. | tests.cpp:19:7:19:17 | smallbuffer | destination buffer |
|
||||
| tests.cpp:172:23:172:31 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:170:17:170:41 | {...} | array |
|
||||
| tests.cpp:176:23:176:30 | access to array | This array indexing operation accesses byte offset 31 but the $@ is only 24 bytes. | tests.cpp:170:17:170:41 | {...} | array |
|
||||
| tests.cpp:222:3:222:8 | call to memset | This 'memset' operation accesses 33 bytes but the $@ is only 32 bytes. | tests.cpp:214:8:214:14 | buffer1 | destination buffer |
|
||||
| tests.cpp:224:3:224:8 | call to memset | This 'memset' operation accesses 33 bytes but the $@ is only 32 bytes. | tests.cpp:215:19:215:30 | new[] | destination buffer |
|
||||
| tests.cpp:226:3:226:8 | call to memset | This 'memset' operation accesses 33 bytes but the $@ is only 32 bytes. | tests.cpp:218:13:218:18 | call to malloc | destination buffer |
|
||||
| tests.cpp:228:3:228:8 | call to memset | This 'memset' operation accesses 33 bytes but the $@ is only 32 bytes. | tests.cpp:218:13:218:18 | call to malloc | destination buffer |
|
||||
| tests.cpp:228:3:228:8 | call to memset | This 'memset' operation accesses 33 bytes but the $@ is only 32 bytes. | tests.cpp:219:13:219:19 | buffer3 | destination buffer |
|
||||
| tests.cpp:231:3:231:8 | call to memcmp | This 'memcmp' operation may access 33 bytes but the $@ is only 32 bytes. | tests.cpp:214:8:214:14 | buffer1 | first buffer |
|
||||
| tests.cpp:231:3:231:8 | call to memcmp | This 'memcmp' operation may access 33 bytes but the $@ is only 32 bytes. | tests.cpp:215:19:215:30 | new[] | second buffer |
|
||||
| tests.cpp:244:2:244:8 | call to memmove | This 'memmove' operation accesses 6 bytes but the $@ is only 5 bytes. | tests.cpp:208:25:208:39 | {...} | source buffer |
|
||||
| tests.cpp:245:2:245:8 | call to memmove | This 'memmove' operation accesses 6 bytes but the $@ is only 5 bytes. | tests.cpp:208:25:208:39 | {...} | destination buffer |
|
||||
| tests.cpp:265:2:265:8 | call to wmemset | This 'wmemset' operation accesses 132 bytes but the $@ is only 128 bytes. | tests.cpp:260:10:260:16 | buffer1 | destination buffer |
|
||||
| tests.cpp:266:2:266:8 | call to wmemset | This 'wmemset' operation accesses 128 bytes but the $@ is only 32 bytes. | tests.cpp:261:7:261:13 | buffer2 | destination buffer |
|
||||
| tests.cpp:275:3:275:8 | call to memset | This 'memset' operation accesses 15 bytes but the $@ is only 14 bytes. | tests.cpp:272:18:272:32 | Hello, world! | destination buffer |
|
||||
| tests.cpp:285:3:285:8 | call to memset | This 'memset' operation accesses 128 bytes but the $@ is only 64 bytes. | tests.cpp:283:12:283:23 | new[] | destination buffer |
|
||||
| tests.cpp:292:3:292:8 | call to memset | This 'memset' operation accesses 11 bytes but the $@ is only 10 bytes. | tests.cpp:289:8:289:12 | array | destination buffer |
|
||||
| tests.cpp:310:2:310:7 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:301:10:301:14 | myVar | destination buffer |
|
||||
| tests.cpp:312:2:312:7 | call to memset | This 'memset' operation accesses 17 bytes but the $@ is only 16 bytes. | tests.cpp:298:7:298:12 | buffer | destination buffer |
|
||||
| tests.cpp:314:2:314:7 | call to memset | This 'memset' operation accesses 8 bytes but the $@ is only 4 bytes. | tests.cpp:299:6:299:10 | field | destination buffer |
|
||||
| tests.cpp:327:3:327:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:301:10:301:14 | myVar | destination buffer |
|
||||
| tests.cpp:327:3:327:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:322:22:322:27 | & ... | destination buffer |
|
||||
| tests.cpp:329:3:329:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:301:10:301:14 | myVar | destination buffer |
|
||||
| tests.cpp:329:3:329:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:322:22:322:27 | & ... | destination buffer |
|
||||
| tests.cpp:329:3:329:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:324:12:324:17 | myPtr1 | destination buffer |
|
||||
| tests.cpp:336:3:336:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:301:10:301:14 | myVar | destination buffer |
|
||||
| tests.cpp:336:3:336:8 | call to memset | This 'memset' operation accesses 21 bytes but the $@ is only 20 bytes. | tests.cpp:333:27:333:32 | & ... | destination buffer |
|
||||
| tests.cpp:346:2:346:14 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:342:7:342:15 | charArray | array |
|
||||
| tests.cpp:349:2:349:14 | access to array | This array indexing operation accesses byte offset 10 but the $@ is only 10 bytes. | tests.cpp:342:7:342:15 | charArray | array |
|
||||
| tests.cpp:350:17:350:29 | access to array | This array indexing operation accesses byte offset 10 but the $@ is only 10 bytes. | tests.cpp:342:7:342:15 | charArray | array |
|
||||
| tests.cpp:352:2:352:13 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:343:6:343:13 | intArray | array |
|
||||
| tests.cpp:355:2:355:13 | access to array | This array indexing operation accesses byte offset 43 but the $@ is only 40 bytes. | tests.cpp:343:6:343:13 | intArray | array |
|
||||
| tests.cpp:356:16:356:27 | access to array | This array indexing operation accesses byte offset 43 but the $@ is only 40 bytes. | tests.cpp:343:6:343:13 | intArray | array |
|
||||
| tests.cpp:358:2:358:16 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:344:11:344:21 | structArray | array |
|
||||
| tests.cpp:361:2:361:16 | access to array | This array indexing operation accesses byte offset 219 but the $@ is only 200 bytes. | tests.cpp:344:11:344:21 | structArray | array |
|
||||
| tests.cpp:362:25:362:39 | access to array | This array indexing operation accesses byte offset 219 but the $@ is only 200 bytes. | tests.cpp:344:11:344:21 | structArray | array |
|
||||
| tests.cpp:365:23:365:34 | access to array | This array indexing operation accesses byte offset 43 but the $@ is only 40 bytes. | tests.cpp:343:6:343:13 | intArray | array |
|
||||
| tests.cpp:373:3:373:13 | access to array | This array indexing operation accesses byte offset 101 but the $@ is only 100 bytes. | tests.cpp:368:47:368:52 | call to malloc | array |
|
||||
| tests.cpp:376:3:376:13 | access to array | This array indexing operation accesses byte offset 101 but the $@ is only 101 bytes. | tests.cpp:369:47:369:52 | call to malloc | array |
|
||||
| tests.cpp:446:3:446:24 | access to array | This array indexing operation accesses a negative index -3 on the $@. | tests.cpp:444:7:444:14 | intArray | array |
|
||||
| tests.cpp:454:3:454:11 | access to array | This array indexing operation accesses a negative index -21 on the $@. | tests.cpp:450:7:450:11 | multi | array |
|
||||
| tests.cpp:456:3:456:11 | access to array | This array indexing operation accesses a negative index -21 on the $@. | tests.cpp:450:7:450:11 | multi | array |
|
||||
| tests.cpp:459:3:459:11 | access to array | This array indexing operation accesses byte offset 639 but the $@ is only 400 bytes. | tests.cpp:450:7:450:11 | multi | array |
|
||||
| tests.cpp:461:3:461:11 | access to array | This array indexing operation accesses byte offset 639 but the $@ is only 400 bytes. | tests.cpp:450:7:450:11 | multi | array |
|
||||
| tests.cpp:476:2:476:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:469:7:469:12 | buffer | array |
|
||||
| tests.cpp:476:2:476:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:470:13:470:18 | buffer | array |
|
||||
| tests.cpp:477:2:477:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:469:7:469:12 | buffer | array |
|
||||
| tests.cpp:477:2:477:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:471:13:471:18 | buffer | array |
|
||||
| tests.cpp:481:2:481:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:469:7:469:12 | buffer | array |
|
||||
| tests.cpp:481:2:481:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:472:13:472:18 | buffer | array |
|
||||
| tests.cpp:487:2:487:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:473:21:473:26 | call to malloc | array |
|
||||
| tests.cpp:491:2:491:7 | access to array | This array indexing operation accesses a negative index -1 on the $@. | tests.cpp:474:21:474:26 | call to malloc | array |
|
||||
| tests.cpp:519:3:519:8 | call to memset | This 'memset' operation accesses 20 bytes but the $@ is only 10 bytes. | tests.cpp:502:15:502:20 | call to malloc | destination buffer |
|
||||
| tests.cpp:519:3:519:8 | call to memset | This 'memset' operation accesses 20 bytes but the $@ is only 10 bytes. | tests.cpp:510:16:510:21 | call to malloc | destination buffer |
|
||||
| tests_restrict.c:12:2:12:7 | call to memcpy | This 'memcpy' operation accesses 2 bytes but the $@ is only 1 byte. | tests_restrict.c:7:6:7:13 | smallbuf | source buffer |
|
||||
| unions.cpp:26:2:26:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:21:10:21:11 | mu | destination buffer |
|
||||
| unions.cpp:30:2:30:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:15:7:15:11 | small | destination buffer |
|
||||
| unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:16:7:16:11 | large | destination buffer |
|
||||
| var_size_struct.cpp:54:5:54:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:32:8:32:10 | str | array |
|
||||
| var_size_struct.cpp:55:5:55:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:38:8:38:10 | str | array |
|
||||
| var_size_struct.cpp:71:3:71:8 | call to memset | This 'memset' operation accesses 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
|
||||
| var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'strncpy' operation may access 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
|
||||
| var_size_struct.cpp:87:3:87:19 | access to array | This array indexing operation accesses byte offset 67 but the $@ is only 64 bytes. | var_size_struct.cpp:78:7:78:14 | elements | array |
|
||||
| var_size_struct.cpp:99:3:99:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
|
||||
| var_size_struct.cpp:101:3:101:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
|
||||
| var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'strncpy' operation may access 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
|
||||
| var_size_struct.cpp:171:3:171:8 | call to memset | This 'memset' operation accesses 100 bytes but the $@ is only 1 byte. | var_size_struct.cpp:125:17:125:19 | arr | destination buffer |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-119/OverflowBuffer.ql
|
||||
@@ -0,0 +1,4 @@
|
||||
| overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
| overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
| overflowdestination.cpp:53:2:53:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
| overflowdestination.cpp:64:2:64:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/OverflowDestination.ql
|
||||
@@ -0,0 +1,10 @@
|
||||
| overflowdestination.cpp:46:20:46:30 | sizeof(<expr>) | Potential buffer-overflow: 'dest' has size 64 not 128. |
|
||||
| tests.cpp:25:33:25:49 | sizeof(<expr>) | Potential buffer-overflow: 'smallbuffer' has size 10 not 20. |
|
||||
| tests.cpp:163:3:163:11 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
|
||||
| tests.cpp:164:8:164:16 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
|
||||
| tests.cpp:245:42:245:42 | 6 | Potential buffer-overflow: 'global_array_5' has size 5 not 6. |
|
||||
| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
|
||||
| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
|
||||
| var_size_struct.cpp:54:5:54:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
|
||||
| var_size_struct.cpp:55:5:55:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
|
||||
| var_size_struct.cpp:103:39:103:41 | 129 | Potential buffer-overflow: 'str' has size 128 not 129. |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/OverflowStatic.ql
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/OverrunWrite.ql
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/OverrunWriteFloat.ql
|
||||
@@ -0,0 +1,3 @@
|
||||
| overflowdestination.cpp:30:2:30:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| tests.cpp:239:3:239:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
| tests.cpp:240:3:240:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/UnboundedWrite.ql
|
||||
@@ -0,0 +1,104 @@
|
||||
// Semmle test cases for rule CWE-119.
|
||||
|
||||
// --- library types, functions etc ---
|
||||
typedef unsigned long size_t;
|
||||
typedef size_t FILE;
|
||||
|
||||
void *malloc(size_t size);
|
||||
char *strncpy(char *s1, const char *s2, size_t n);
|
||||
size_t strlen(const char *s);
|
||||
char *fgets(char *s, int n, FILE *stream);
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
|
||||
// --- example from the qhelp ---
|
||||
|
||||
inline size_t min(size_t a, size_t b) {
|
||||
if (a < b) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char param[20];
|
||||
char *arg1;
|
||||
|
||||
arg1 = argv[1];
|
||||
|
||||
//wrong: only uses the size of the source (argv[1]) when using strncpy
|
||||
strncpy(param, arg1, strlen(arg1));
|
||||
|
||||
//correct: uses the size of the destination array as well
|
||||
strncpy(param, arg1, min(strlen(arg1), sizeof(param) -1));
|
||||
}
|
||||
|
||||
// --- test cases ---
|
||||
|
||||
void overflowdest_test1(FILE *f)
|
||||
{
|
||||
char dest[64];
|
||||
char src[128];
|
||||
|
||||
fgets(src, 128, f); // GOOD (taints `src`)
|
||||
|
||||
memcpy(dest, src, sizeof(dest)); // GOOD
|
||||
memcpy(dest, src, sizeof(src)); // BAD: size derived from the source buffer
|
||||
memcpy(dest, dest, sizeof(dest)); // GOOD
|
||||
}
|
||||
|
||||
void overflowdest_test2(FILE *f, char *dest, char *src)
|
||||
{
|
||||
memcpy(dest, src, strlen(dest) + 1); // GOOD
|
||||
memcpy(dest, src, strlen(src) + 1); // BAD: size derived from the source buffer
|
||||
memcpy(dest, dest, strlen(dest) + 1); // GOOD
|
||||
}
|
||||
|
||||
void overflowdest_test3(FILE *f, char *dest, char *src)
|
||||
{
|
||||
char *dest2 = dest;
|
||||
char *src2 = src;
|
||||
char *src3 = src;
|
||||
|
||||
memcpy(dest2, src2, strlen(dest2) + 1); // GOOD
|
||||
memcpy(dest2, src2, strlen(src2) + 1); // BAD: size derived from the source buffer
|
||||
memcpy(dest2, dest2, strlen(dest2) + 1); // GOOD
|
||||
}
|
||||
|
||||
void overflowdest_test23_caller(FILE *f)
|
||||
{
|
||||
char dest[64];
|
||||
char src[128];
|
||||
|
||||
fgets(src, 128, f); // GOOD (taints `src`)
|
||||
|
||||
overflowdest_test2(f, dest, src);
|
||||
overflowdest_test3(f, dest, src);
|
||||
}
|
||||
|
||||
void overflowdest_test4(FILE *f, int destSize, int srcSize)
|
||||
{
|
||||
char *dest = (char *)malloc(destSize);
|
||||
char *src = (char *)malloc(srcSize);
|
||||
|
||||
fgets(src, srcSize, f); // GOOD (taints `src`)
|
||||
|
||||
memcpy(dest, src, destSize); // GOOD
|
||||
memcpy(dest, src, srcSize); // BAD: size derived from the source buffer [NOT DETECTED]
|
||||
memcpy(dest, dest, destSize); // GOOD
|
||||
}
|
||||
|
||||
class OverflowDestClass5 {
|
||||
public:
|
||||
unsigned int bufferSize;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
void overflowdest_test5(FILE *f, OverflowDestClass5 &dest, const OverflowDestClass5 &src)
|
||||
{
|
||||
fgets(src.buffer, src.bufferSize, f); // GOOD (taints `src`)
|
||||
|
||||
memcpy(dest.buffer, src.buffer, dest.bufferSize); // GOOD
|
||||
memcpy(dest.buffer, src.buffer, src.bufferSize); // BAD: size derived from the source buffer [NOT DETECTED]
|
||||
memcpy(dest.buffer, dest.buffer, dest.bufferSize); // GOOD
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
//semmle-extractor-options: --edg --target --edg linux_x86_64
|
||||
|
||||
// Semmle test cases for rule CWE-119.
|
||||
|
||||
// library types, functions etc
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
void *memmove(void *s1, const void *s2, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
int memcmp(const void *s1, const void *s2, size_t n);
|
||||
int printf(const char *format, ...);
|
||||
size_t strlen(const char *s);
|
||||
char *strncpy(char *s1, const char *s2, size_t n);
|
||||
|
||||
void test1()
|
||||
{
|
||||
char smallbuffer[10];
|
||||
char bigbuffer[20];
|
||||
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(bigbuffer)); // BAD: over-read
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(bigbuffer)); // BAD: over-write
|
||||
}
|
||||
|
||||
void test2()
|
||||
{
|
||||
char *smallbuffer = (char *)malloc(sizeof(char) * 10);
|
||||
char *bigbuffer = (char *)malloc(sizeof(char) * 20);
|
||||
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(bigbuffer)); // BAD: over-read [NOT DETECTED]
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(bigbuffer)); // BAD: over-write [NOT DETECTED]
|
||||
|
||||
free(bigbuffer);
|
||||
free(smallbuffer);
|
||||
}
|
||||
|
||||
void test3()
|
||||
{
|
||||
char *smallbuffer, *bigbuffer;
|
||||
|
||||
smallbuffer = new char[10];
|
||||
bigbuffer = new char[20];
|
||||
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(bigbuffer, smallbuffer, sizeof(bigbuffer)); // BAD: over-read [NOT DETECTED]
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(smallbuffer)); // GOOD
|
||||
memcpy(smallbuffer, bigbuffer, sizeof(bigbuffer)); // BAD: over-write [NOT DETECTED]
|
||||
|
||||
delete [] bigbuffer;
|
||||
delete [] smallbuffer;
|
||||
}
|
||||
|
||||
void test4(int unbounded)
|
||||
{
|
||||
int bounded = 100;
|
||||
char buffer1[100], buffer2[100];
|
||||
|
||||
memmove(buffer1, buffer2, bounded); // GOOD
|
||||
memmove(buffer1, buffer2, unbounded); // BAD: may over-write [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test5(int unbounded)
|
||||
{
|
||||
int a, b, c, d, e;
|
||||
char buffer[100];
|
||||
|
||||
buffer[unbounded] = 'x'; // BAD: may under- or over-write [NOT DETECTED]
|
||||
buffer[0] = buffer[unbounded]; // BAD: may under- or over-read [NOT DETECTED]
|
||||
|
||||
a = unbounded;
|
||||
buffer[a] = 'x'; // BAD: may under- or over-write [NOT DETECTED]
|
||||
buffer[0] = buffer[a]; // BAD: may under- or over-read [NOT DETECTED]
|
||||
|
||||
b = unbounded;
|
||||
if (b < 0) {b = 0;}
|
||||
buffer[b] = 'x'; // BAD: may over-write [NOT DETECTED]
|
||||
buffer[0] = buffer[b]; // BAD: may over-read [NOT DETECTED]
|
||||
|
||||
c = unbounded;
|
||||
if (c > 99) {c = 99;}
|
||||
buffer[c] = 'x'; // BAD: may under-write [NOT DETECTED]
|
||||
buffer[0] = buffer[c]; // BAD: may under-read [NOT DETECTED]
|
||||
|
||||
d = unbounded;
|
||||
if (d < 0) {d = 0;}
|
||||
if (d > 99) {d = 99;}
|
||||
buffer[d] = 'x'; // GOOD
|
||||
buffer[0] = buffer[d]; // GOOD
|
||||
|
||||
e = unbounded;
|
||||
e = 50;
|
||||
buffer[e] = 'x'; // GOOD
|
||||
buffer[0] = buffer[e]; // GOOD
|
||||
}
|
||||
|
||||
void test6(bool cond)
|
||||
{
|
||||
int a, b, c, d, e, f, g, h, i, j, k;
|
||||
char buffer[100];
|
||||
char ch;
|
||||
|
||||
a = -1;
|
||||
buffer[a] = 'x'; // BAD: under-write [NOT DETECTED]
|
||||
ch = buffer[a]; // BAD: under-read [NOT DETECTED]
|
||||
|
||||
b = 0;
|
||||
buffer[b] = 'x'; // GOOD
|
||||
ch = buffer[b]; // GOOD
|
||||
|
||||
c = 100;
|
||||
buffer[c] = 'x'; // BAD: over-write [NOT DETECTED]
|
||||
ch = buffer[c]; // BAD: under-read [NOT DETECTED]
|
||||
|
||||
d = 0;
|
||||
d = 1000;
|
||||
buffer[d] = 'x'; // BAD: over-write [NOT DETECTED]
|
||||
ch = buffer[d]; // BAD: over-read [NOT DETECTED]
|
||||
|
||||
e = 1000;
|
||||
e = 0;
|
||||
buffer[e] = 'x'; // GOOD
|
||||
ch = buffer[e]; // GOOD
|
||||
|
||||
f = 0;
|
||||
if (cond) {f = 1000;}
|
||||
buffer[f] = 'x'; // BAD: may over-write [NOT DETECTED]
|
||||
ch = buffer[f]; // BAD: may over-read [NOT DETECTED]
|
||||
|
||||
g = 1000;
|
||||
if (cond) {g = 0;}
|
||||
buffer[g] = 'x'; // BAD: may over-write [NOT DETECTED]
|
||||
ch = buffer[g]; // BAD: may over-read [NOT DETECTED]
|
||||
|
||||
h = 1000;
|
||||
if (cond)
|
||||
{
|
||||
h = 10;
|
||||
} else {
|
||||
h = 20;
|
||||
}
|
||||
buffer[h] = 'x'; // GOOD
|
||||
ch = buffer[h]; // GOOD
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
buffer[i] = 'x'; // GOOD
|
||||
ch = buffer[i]; // GOOD
|
||||
}
|
||||
|
||||
for (j = -1; j < 100; j++)
|
||||
{
|
||||
buffer[j] = 'x'; // BAD: under-write [NOT DETECTED]
|
||||
ch = buffer[j]; // BAD: under-read [NOT DETECTED]
|
||||
}
|
||||
|
||||
for (k = 0; k <= 100; k++)
|
||||
{
|
||||
buffer[k] = 'x'; // BAD: over-write
|
||||
ch = buffer[k]; // BAD: over-read
|
||||
}
|
||||
}
|
||||
|
||||
void test7()
|
||||
{
|
||||
char *names[] = {"tom", "dick", "harry"};
|
||||
|
||||
printf("name: %s\n", names[-1]); // BAD: under-read
|
||||
printf("name: %s\n", names[0]); // GOOD
|
||||
printf("name: %s\n", names[1]); // GOOD
|
||||
printf("name: %s\n", names[2]); // GOOD
|
||||
printf("name: %s\n", names[3]); // BAD: over-read
|
||||
}
|
||||
|
||||
void test8(int unbounded)
|
||||
{
|
||||
char buffer[128];
|
||||
int v1 = 128;
|
||||
int v2 = 256;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v1; i++)
|
||||
{
|
||||
buffer[i] = 0; // GOOD
|
||||
}
|
||||
|
||||
for (i = 0; i < v2; i++)
|
||||
{
|
||||
buffer[i] = 0; // BAD: over-write [NOT DETECTED]
|
||||
}
|
||||
|
||||
for (i = 0; i < unbounded; i++)
|
||||
{
|
||||
buffer[i] = 0; // BAD: may over-write [NOT DETECTED]
|
||||
}
|
||||
|
||||
unbounded = 128;
|
||||
for (i = 0; i < unbounded; i++)
|
||||
{
|
||||
buffer[i] = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
char global_array_5[] = {1, 2, 3, 4, 5};
|
||||
char global_array_6[] = {1, 2, 3, 4, 5, 6};
|
||||
|
||||
void test9(int param)
|
||||
{
|
||||
{
|
||||
char buffer1[32];
|
||||
char *buffer2 = new char[32];
|
||||
void *buffer3;
|
||||
void *buffer4;
|
||||
buffer3 = malloc(32);
|
||||
buffer4 = buffer3;
|
||||
|
||||
memset(buffer1, 0, 32); // GOOD
|
||||
memset(buffer1, 0, 33); // BAD: overrun write of buffer1
|
||||
memset(buffer2, 0, 32); // GOOD
|
||||
memset(buffer2, 0, 33); // BAD: overrun write of buffer2
|
||||
memset(buffer3, 0, 32); // GOOD
|
||||
memset(buffer3, 0, 33); // BAD: overrun write of buffer3
|
||||
memset(buffer4, 0, 32); // GOOD
|
||||
memset(buffer4, 0, 33); // BAD: overrun write of buffer4 (buffer3)
|
||||
|
||||
memcmp(buffer1, buffer2, 32); // GOOD
|
||||
memcmp(buffer1, buffer2, 33); // BAD: overrun read of buffer1, buffer2
|
||||
}
|
||||
|
||||
{
|
||||
char *str1 = "1234567";
|
||||
char *str2 = "abcdefgh";
|
||||
|
||||
strncpy(str1, str2, strlen(str1) + 1); // GOOD
|
||||
strncpy(str1, str2, strlen(str2) + 1); // BAD: overrun write of str1
|
||||
strncpy(str2, str1, strlen(str1) + 1); // DUBIOUS (detected)
|
||||
strncpy(str2, str1, strlen(str2) + 1); // BAD: overrun read of str1 [NOT REPORTED]
|
||||
}
|
||||
|
||||
memmove(global_array_6, global_array_5, 6); // BAD: overrun read of global_array_5
|
||||
memmove(global_array_5, global_array_6, 6); // BAD: overrun write of global_array_5
|
||||
|
||||
if (param > 0)
|
||||
{
|
||||
void *buffer = malloc(param);
|
||||
|
||||
memset(buffer, 0xFF, param); // GOOD
|
||||
memset(buffer, 0xFF, param * 2); // BAD: overrun write of buffer [NOT REPORTED]
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *wmemset(wchar_t *s, wchar_t c, size_t n);
|
||||
|
||||
void test10()
|
||||
{
|
||||
wchar_t buffer1[32];
|
||||
char buffer2[32];
|
||||
|
||||
|
||||
wmemset(buffer1, 0, 32); // GOOD
|
||||
wmemset(buffer1, 0, 33); // BAD: overrun write of buffer1
|
||||
wmemset((wchar_t *)buffer2, 0, 32); // BAD: overrun write of buffer2
|
||||
}
|
||||
|
||||
void test11()
|
||||
{
|
||||
{
|
||||
char *string = "Hello, world!";
|
||||
|
||||
memset(string, 0, 14); // GOOD
|
||||
memset(string, 0, 15); // BAD: overrun write of string
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = new char[128];
|
||||
|
||||
memset(buffer, 0, 128);
|
||||
|
||||
buffer = new char[64];
|
||||
|
||||
memset(buffer, 0, 128); // BAD: overrun write of buffer
|
||||
}
|
||||
|
||||
{
|
||||
char array[10] = "123";
|
||||
|
||||
memset(array, 0, 10); // GOOD
|
||||
memset(array, 0, 11); // BAD: overrun write of array
|
||||
}
|
||||
}
|
||||
|
||||
struct myStruct
|
||||
{
|
||||
char buffer[16];
|
||||
int field;
|
||||
};
|
||||
myStruct myVar;
|
||||
|
||||
void test12()
|
||||
{
|
||||
char buf[16];
|
||||
char *dbuf;
|
||||
dbuf = new char[16];
|
||||
|
||||
memset(&myVar, 0, sizeof(myVar)); // GOOD
|
||||
memset(&myVar, 0, sizeof(myVar) + 1); // BAD: overrun write of myVar
|
||||
memset(myVar.buffer, 0, 16); // GOOD
|
||||
memset(myVar.buffer, 0, 17); // BAD: overrun write of myVar.buffer
|
||||
memset(&(myVar.field), 0, sizeof(int)); // GOOD
|
||||
memset(&(myVar.field), 0, sizeof(int) * 2); // BAD: overrun write of myVar.field
|
||||
|
||||
memset(buf + 8, 0, 8); // GOOD
|
||||
memset(buf + 8, 0, 9); // BAD: overrun write of buf [NOT DETECTED]
|
||||
memset(dbuf + 8, 0, 8); // GOOD
|
||||
memset(dbuf + 8, 0, 9); // BAD: overrun write of dbuf [NOT DETECTED]
|
||||
|
||||
{
|
||||
myStruct *myPtr1 = &myVar;
|
||||
myStruct *myPtr2;
|
||||
myPtr2 = myPtr1;
|
||||
|
||||
memset(myPtr1, 0, sizeof(myStruct)); // GOOD
|
||||
memset(myPtr1, 0, sizeof(myStruct) + 1); // BAD: overrun write of myVar
|
||||
memset(myPtr2, 0, sizeof(myStruct)); // GOOD
|
||||
memset(myPtr2, 0, sizeof(myStruct) + 1); // BAD: overrun write of myVar
|
||||
}
|
||||
|
||||
{
|
||||
void *myPtr3 = (void *)(&myVar);
|
||||
|
||||
memset(myPtr3, 0, sizeof(myStruct)); // GOOD
|
||||
memset(myPtr3, 0, sizeof(myStruct) + 1); // BAD: overrun write of myVar
|
||||
}
|
||||
}
|
||||
|
||||
void test13()
|
||||
{
|
||||
char charArray[10];
|
||||
int intArray[10];
|
||||
myStruct structArray[10];
|
||||
|
||||
charArray[-1] = 1; // BAD: underrun write
|
||||
charArray[0] = 1; // GOOD
|
||||
charArray[9] = 1; // GOOD
|
||||
charArray[10] = 1; // BAD: overrun write
|
||||
charArray[5] = charArray[10]; // BAD: overrun read
|
||||
|
||||
intArray[-1] = 1; // BAD: underrun write
|
||||
intArray[0] = 1; // GOOD
|
||||
intArray[9] = 1; // GOOD
|
||||
intArray[10] = 1; // BAD: overrun write
|
||||
intArray[5] = intArray[10]; // BAD: overrun read
|
||||
|
||||
structArray[-1].field = 1; // BAD: underrun write
|
||||
structArray[0].field = 1; // GOOD
|
||||
structArray[9].field = 1; // GOOD
|
||||
structArray[10].field = 1; // BAD: overrun write
|
||||
structArray[5].field = structArray[10].field; // BAD: overrun read
|
||||
|
||||
charArray[9] = (char)intArray[9]; // GOOD
|
||||
charArray[9] = (char)intArray[10]; // BAD: overrun read
|
||||
|
||||
{
|
||||
unsigned short *buffer1 = (unsigned short *)malloc(sizeof(short) * 50);
|
||||
unsigned short *buffer2 = (unsigned short *)malloc(101); // 50.5 shorts
|
||||
|
||||
buffer1[0] = 0xFFFF;
|
||||
buffer1[49] = 0xFFFF;
|
||||
buffer1[50] = 0xFFFF; // BAD: overrun write
|
||||
buffer2[0] = 0xFFFF;
|
||||
buffer2[49] = 0xFFFF;
|
||||
buffer2[50] = 0xFFFF; // BAD: overrun write
|
||||
}
|
||||
}
|
||||
|
||||
int strncmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
const char *get_buffer();
|
||||
void get_line(const char **ptr);
|
||||
|
||||
void test14()
|
||||
{
|
||||
const char *buf;
|
||||
|
||||
buf = get_buffer();
|
||||
|
||||
if (buf != 0 && memcmp(buf, "mydata", 6) == 0) // GOOD
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void test15()
|
||||
{
|
||||
const char *ptr = get_buffer();
|
||||
int i;
|
||||
|
||||
for (i = 0; ; i++)
|
||||
{
|
||||
get_line(&ptr);
|
||||
|
||||
if (strncmp(ptr, "token", 5) == 0)
|
||||
{
|
||||
if (ptr[5] == ' ') // GOOD
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct mystruct16
|
||||
{
|
||||
int data;
|
||||
struct mystruct16 *next;
|
||||
};
|
||||
|
||||
void test16()
|
||||
{
|
||||
int arr1[10];
|
||||
int *ptr1_1 = &arr1[9]; // GOOD
|
||||
int *ptr1_2 = &arr1[10]; // GOOD: valid as long as we don't dereference it
|
||||
int *ptr1_3 = &arr1[11]; // BAD: not guaranteed to be valid [NOT DETECTED - considered outside the scope of CWE-119]
|
||||
|
||||
int x = *--ptr1_2; // GOOD
|
||||
|
||||
mystruct16 arr2[10];
|
||||
mystruct16 *ptr2_1 = &arr2[9]; // GOOD
|
||||
mystruct16 *ptr2_2 = &arr2[10]; // GOOD: valid as long as we don't dereference it
|
||||
mystruct16 *ptr2_3 = &arr2[11]; // BAD: not guaranteed to be valid [NOT DETECTED - considered outside the scope of CWE-119]
|
||||
|
||||
(--ptr2_2)->data = 1; // GOOD
|
||||
}
|
||||
|
||||
void test17(long long *longArray)
|
||||
{
|
||||
longArray[-1] = -1; // BAD: underrun write [NOT DETECTED]
|
||||
|
||||
{
|
||||
int intArray[5];
|
||||
|
||||
((char *)intArray)[-3] = 0; // BAD: underrun write
|
||||
}
|
||||
|
||||
{
|
||||
int multi[10][10];
|
||||
|
||||
multi[5][5] = 0; // GOOD
|
||||
|
||||
multi[-5][5] = 0; // BAD: underrun write [INCORRECT MESSAGE]
|
||||
multi[5][-5] = 0; // DUBIOUS: underrun write (this one is still within the bounds of the whole array)
|
||||
multi[-5][-5] = 0; // BAD: underrun write [INCORRECT MESSAGE]
|
||||
multi[0][-5] = 0; // BAD: underrun write [NOT DETECTED]
|
||||
|
||||
multi[15][5] = 0; // BAD: overrun write
|
||||
multi[5][15] = 0; // DUBIOUS: overrun write (this one is still within the bounds of the whole array)
|
||||
multi[15][15] = 0; // BAD: overrun write
|
||||
}
|
||||
}
|
||||
|
||||
char *update(char *ptr);
|
||||
|
||||
void test18()
|
||||
{
|
||||
char buffer[128];
|
||||
char *p1 = buffer;
|
||||
char *p2 = buffer;
|
||||
char *p3 = buffer;
|
||||
char *p4 = (char *)malloc(128);
|
||||
char *p5 = (char *)malloc(128);
|
||||
|
||||
p1[-1] = 0; // BAD: underrun write
|
||||
p2[-1] = 0; // BAD: underrun write
|
||||
p2++;
|
||||
p2[-1] = 0; // GOOD
|
||||
|
||||
p3[-1] = 0; // BAD
|
||||
while (*p3 != 0) {
|
||||
p3 = update(p3);
|
||||
}
|
||||
p3[-1] = 0; // GOOD
|
||||
|
||||
p4[-1] = 0; // BAD: underrun write
|
||||
p4++;
|
||||
p4[-1] = 0; // GOOD
|
||||
|
||||
p5[-1] = 0; // BAD
|
||||
while (*p5 != 0) {
|
||||
p5 = update(p5);
|
||||
}
|
||||
p5[-1] = 0; // GOOD
|
||||
}
|
||||
|
||||
void test19(bool b)
|
||||
{
|
||||
char *p1, *p2, *p3;
|
||||
|
||||
p1 = (char *)malloc(10);
|
||||
p2 = (char *)malloc(10);
|
||||
p3 = (char *)malloc(20);
|
||||
|
||||
// ...
|
||||
|
||||
if (b)
|
||||
{
|
||||
p1 = (char *)malloc(10);
|
||||
p2 = (char *)malloc(20);
|
||||
p3 = (char *)malloc(20);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
if (b)
|
||||
{
|
||||
memset(p1, 0, 20); // BAD
|
||||
memset(p2, 0, 20); // GOOD
|
||||
memset(p3, 0, 20); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
long long arr17[19];
|
||||
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4(argc);
|
||||
test5(argc);
|
||||
test6(argc == 0);
|
||||
test7();
|
||||
test8(argc);
|
||||
test9(argc);
|
||||
test10();
|
||||
test11();
|
||||
test12();
|
||||
test15();
|
||||
test16();
|
||||
test17(arr17);
|
||||
test18();
|
||||
test19(argc == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//semmle-extractor-options: --edg --target --edg linux_x86_64 -std=c99
|
||||
|
||||
// library types, functions etc
|
||||
typedef unsigned long size_t;
|
||||
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
|
||||
|
||||
char smallbuf[1], largebuf[2];
|
||||
|
||||
void test1()
|
||||
{
|
||||
memcpy(largebuf, smallbuf, 1); // GOOD
|
||||
memcpy(largebuf, smallbuf, 2); // BAD: source over-read
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
test1();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//semmle-extractor-options: --edg --target --edg linux_x86_64
|
||||
|
||||
// Semmle test cases for rule CWE-119 involving unions.
|
||||
|
||||
// library types, functions etc
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
|
||||
// --- unions ---
|
||||
|
||||
union myUnion {
|
||||
char small[10];
|
||||
char large[100];
|
||||
};
|
||||
|
||||
void myUnionTest()
|
||||
{
|
||||
myUnion mu;
|
||||
|
||||
memset(&mu, 0, sizeof(mu));
|
||||
memset(&mu, 0, sizeof(mu.small));
|
||||
memset(&mu, 0, sizeof(mu.large));
|
||||
memset(&mu, 0, 200); // BAD
|
||||
memset(&(mu.small), 0, sizeof(mu));
|
||||
memset(&(mu.small), 0, sizeof(mu.small));
|
||||
memset(&(mu.small), 0, sizeof(mu.large)); // (dubious)
|
||||
memset(&(mu.small), 0, 200); // BAD
|
||||
memset(&(mu.large), 0, sizeof(mu));
|
||||
memset(&(mu.large), 0, sizeof(mu.small)); // (dubious)
|
||||
memset(&(mu.large), 0, sizeof(mu.large));
|
||||
memset(&(mu.large), 0, 200); // BAD
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
struct Header {
|
||||
int f1;
|
||||
int f2;
|
||||
int f3;
|
||||
int f4;
|
||||
};
|
||||
|
||||
union FileBuf {
|
||||
char buf[0x10000];
|
||||
struct Header header;
|
||||
};
|
||||
|
||||
void fileBufTest(FileBuf *buf) {
|
||||
Header* h = (Header *)malloc(sizeof(FileBuf));
|
||||
memcpy(h, &(buf->header), sizeof(FileBuf)); // GOOD (h, buf are both the size of FileBuf)
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
//semmle-extractor-options: --edg --target --edg linux_x86_64
|
||||
|
||||
// Semmle test cases for rule CWE-119 involving variable size structs.
|
||||
|
||||
// library types, functions etc
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
char *strncpy(char *s1, const char *s2, size_t n);
|
||||
|
||||
// --- tests ---
|
||||
|
||||
struct VarString1 {
|
||||
int length;
|
||||
char str[1];
|
||||
};
|
||||
|
||||
struct VarString2 {
|
||||
int length;
|
||||
char str[1];
|
||||
void f(void) {return;};
|
||||
};
|
||||
|
||||
struct VarString3 {
|
||||
int length;
|
||||
char str[1];
|
||||
bool operator==(const struct VarString3 &other) const { return true; }
|
||||
};
|
||||
|
||||
struct VarString4 {
|
||||
int length;
|
||||
char str[1];
|
||||
int i;
|
||||
};
|
||||
|
||||
struct VarString5 {
|
||||
int length;
|
||||
char str[1];
|
||||
char str2[1];
|
||||
};
|
||||
|
||||
void testVarString(int n) {
|
||||
if (n >= 2)
|
||||
{
|
||||
VarString1* s1 = (VarString1*)malloc(sizeof(VarString1) + n);
|
||||
VarString2* s2 = (VarString2*)malloc(sizeof(VarString2) + n);
|
||||
VarString3* s3 = (VarString3*)malloc(sizeof(VarString3) + n);
|
||||
VarString4* s4 = (VarString4*)malloc(sizeof(VarString4) + n);
|
||||
VarString5* s5 = (VarString5*)malloc(sizeof(VarString5) + n);
|
||||
|
||||
s1->str[1] = '?'; // GOOD
|
||||
s2->str[1] = '?'; // GOOD
|
||||
s3->str[1] = '?'; // GOOD
|
||||
s4->str[1] = '?'; // BAD
|
||||
s5->str[1] = '?'; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
struct varStruct1 {
|
||||
int amount;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
void testVarStruct1() {
|
||||
varStruct1 *vs1 = (varStruct1 *)malloc(sizeof(varStruct1) + 1024);
|
||||
|
||||
vs1->amount = 1024;
|
||||
memset(vs1->data, 0, 1024); // GOOD
|
||||
memset(vs1->data, 0, 1025); // BAD: buffer overflow
|
||||
strncpy(vs1->data, "Hello, world!", 1024); // GOOD
|
||||
strncpy(vs1->data, "Hello, world!", 1025); // BAD
|
||||
}
|
||||
|
||||
struct varStruct2 {
|
||||
int size;
|
||||
int elements[];
|
||||
};
|
||||
|
||||
void testVarStruct2() {
|
||||
varStruct2 *vs2 = (varStruct2 *)malloc(sizeof(varStruct2) + (16 * sizeof(int)));
|
||||
int i;
|
||||
|
||||
vs2->size = 16;
|
||||
vs2->elements[15] = 0; // GOOD
|
||||
vs2->elements[16] = 0; // BAD: buffer overflow
|
||||
}
|
||||
|
||||
struct notVarStruct1 {
|
||||
int length;
|
||||
char str[128];
|
||||
};
|
||||
|
||||
void testNotVarStruct1() {
|
||||
notVarStruct1 *nvs1 = (notVarStruct1 *)malloc(sizeof(notVarStruct1) * 2);
|
||||
|
||||
memset(nvs1->str, 0, 128); // GOOD
|
||||
memset(nvs1->str, 0, 129); // BAD: buffer overflow
|
||||
memset(nvs1[1].str, 0, 128); // GOOD
|
||||
memset(nvs1[1].str, 0, 129); // BAD: buffer overflow
|
||||
strncpy(nvs1->str, "Hello, world!", 128); // GOOD
|
||||
strncpy(nvs1->str, "Hello, world!", 129); // BAD
|
||||
}
|
||||
|
||||
struct notVarStruct2 {
|
||||
char str[0];
|
||||
int length;
|
||||
};
|
||||
|
||||
void testNotVarStruct2() {
|
||||
notVarStruct2 *nvs2 = (notVarStruct2 *)malloc(sizeof(notVarStruct2) + 128);
|
||||
|
||||
nvs2->length = 200;
|
||||
nvs2->str[0] = '?'; // BAD: buffer overflow [NOT DETECTED]
|
||||
nvs2->str[1] = '?'; // BAD: buffer overflow [NOT DETECTED]
|
||||
}
|
||||
|
||||
struct varStruct3 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
struct varStruct4 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
struct varStruct5 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
struct varStruct6 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
struct varStruct7 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
struct varStruct8 {
|
||||
int a, b, c, d;
|
||||
float arr[1];
|
||||
};
|
||||
struct varStruct9 {
|
||||
int a, b, c, d;
|
||||
unsigned char arr[1];
|
||||
};
|
||||
|
||||
#define offsetof(type, memberdesignator) (size_t)(&((type*)0)->memberdesignator)
|
||||
|
||||
size_t sizeForVarStruct7(unsigned int array_size)
|
||||
{
|
||||
return sizeof(varStruct7) + (sizeof(unsigned char) * array_size) - sizeof(unsigned char);
|
||||
}
|
||||
|
||||
void useVarStruct34(varStruct5 *vs5) {
|
||||
varStruct3 *vs3a = (varStruct3 *)malloc(sizeof(varStruct3) + 9); // establish varStruct3 as variable size
|
||||
varStruct3 *vs3b = (varStruct3 *)malloc(sizeof(varStruct3));
|
||||
varStruct4 *vs4a = (varStruct4 *)malloc(sizeof(varStruct4));
|
||||
varStruct4 *vs4b = (varStruct4 *)malloc(sizeof(varStruct4));
|
||||
varStruct5 *vs5a = (varStruct5 *)malloc(sizeof(*vs5) + 9); // establish varStruct5 as variable size
|
||||
varStruct5 *vs5b = (varStruct5 *)malloc(sizeof(*vs5));
|
||||
varStruct6 *vs6 = (varStruct6 *)malloc(offsetof(varStruct6, arr) + 9); // establish varStruct6 as variable size
|
||||
varStruct7 *vs7 = (varStruct7 *)malloc(sizeForVarStruct7(9)); // establish varStruct7 as variable size
|
||||
varStruct8 *vs8a = (varStruct8 *)malloc(sizeof(varStruct8) + 9); // establish varStruct8 as variable size
|
||||
varStruct8 *vs8b = (varStruct8 *)malloc(sizeof(varStruct8));
|
||||
varStruct9 *vs9 = (varStruct9 *)malloc(__builtin_offsetof(varStruct9, arr) + 9); // establish varStruct9 as variable size
|
||||
}
|
||||
|
||||
void testVarStruct34(varStruct3 *vs3, varStruct4 *vs4, varStruct5 *vs5, varStruct6 *vs6, varStruct7 *vs7, varStruct8 *vs8, varStruct9 *vs9) {
|
||||
memset(vs3->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
memset(vs4->arr, 'x', 100); // BAD: it's not variable size, so this is a buffer overflow
|
||||
memset(vs5->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
memset(vs6->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
memset(vs7->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
memset(vs8->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
memset(vs9->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
#define NUMINTS (10)
|
||||
|
||||
struct PseudoUnion {
|
||||
unsigned int flags;
|
||||
char data[0];
|
||||
int vals[NUMINTS];
|
||||
};
|
||||
|
||||
void usePseudoUnion(PseudoUnion *pu, char *data) {
|
||||
// clear via pu->vals
|
||||
memset(pu->vals, 0, sizeof(int) * NUMINTS); // GOOD
|
||||
|
||||
// fill via pu->data
|
||||
strncpy((char *)(pu->vals), data, sizeof(int) * NUMINTS); // GOOD
|
||||
|
||||
// clear via pu->data
|
||||
memset(pu->data, 0, sizeof(int) * NUMINTS); // GOOD
|
||||
|
||||
// fill via pu->data
|
||||
strncpy(pu->data, data, sizeof(int) * NUMINTS); // GOOD
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
| var_size_struct.cpp:13:8:13:17 | VarString1 | var_size_struct.cpp:15:8:15:10 | str |
|
||||
| var_size_struct.cpp:18:8:18:17 | VarString2 | var_size_struct.cpp:20:8:20:10 | str |
|
||||
| var_size_struct.cpp:24:8:24:17 | VarString3 | var_size_struct.cpp:26:8:26:10 | str |
|
||||
| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:39:8:39:11 | str2 |
|
||||
| var_size_struct.cpp:61:8:61:17 | varStruct1 | var_size_struct.cpp:63:8:63:11 | data |
|
||||
| var_size_struct.cpp:76:8:76:17 | varStruct2 | var_size_struct.cpp:78:7:78:14 | elements |
|
||||
| var_size_struct.cpp:119:8:119:17 | varStruct3 | var_size_struct.cpp:121:17:121:19 | arr |
|
||||
| var_size_struct.cpp:127:8:127:17 | varStruct5 | var_size_struct.cpp:129:17:129:19 | arr |
|
||||
| var_size_struct.cpp:131:8:131:17 | varStruct6 | var_size_struct.cpp:133:17:133:19 | arr |
|
||||
| var_size_struct.cpp:135:8:135:17 | varStruct7 | var_size_struct.cpp:137:17:137:19 | arr |
|
||||
| var_size_struct.cpp:139:8:139:17 | varStruct8 | var_size_struct.cpp:141:9:141:11 | arr |
|
||||
| var_size_struct.cpp:143:8:143:17 | varStruct9 | var_size_struct.cpp:145:17:145:19 | arr |
|
||||
@@ -0,0 +1,6 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Buffer
|
||||
|
||||
from Class c, MemberVariable v
|
||||
where memberMayBeVarSize(c, v)
|
||||
select c, v
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.c:20:2:20:7 | call to strcat | Always check the size of the source buffer when using strcat. |
|
||||
| test.c:33:5:33:10 | call to strcat | Always check the size of the source buffer when using strcat. |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Semmle test case for UnsafeUseOfStrcat.ql
|
||||
Associated with CWE-120 http://cwe.mitre.org/data/definitions/120.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
char *strcat(char *s1, const char *s2);
|
||||
size_t strlen(const char *s);
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
static void bad0(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s: ");
|
||||
strcat(buf, s); // BAD -- s may be too long and overflow the buffer
|
||||
}
|
||||
|
||||
static void good0(char *s) {
|
||||
char buf[80];
|
||||
strcpy(buf, "s: ");
|
||||
if(strlen(s) < 77)
|
||||
strcat(buf, s); // GOOD
|
||||
}
|
||||
|
||||
static void bad1(char *s, int len) {
|
||||
char *buf = malloc(len+4);
|
||||
strcpy(buf, "s: ");
|
||||
strcat(buf, s); // BAD -- s may be too long and overflow the buffer
|
||||
}
|
||||
|
||||
static void good1(char *s, int len) {
|
||||
char *buf = malloc(len+4);
|
||||
strcpy(buf, "s: ");
|
||||
if (strlen(s) <= len)
|
||||
strcat(buf, s); // GOOD
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| tests.c:43:3:43:10 | call to snprintf | This 'call to snprintf' operation is limited to 111 bytes but the destination is only 110 bytes. |
|
||||
| tests.c:46:3:46:10 | call to snprintf | This 'call to snprintf' operation is limited to 111 bytes but the destination is only 110 bytes. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/BadlyBoundedWrite.ql
|
||||
@@ -0,0 +1,13 @@
|
||||
| tests.c:54:3:54:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:58:3:58:9 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:62:17:62:24 | buffer10 | This 'scanf string argument' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:63:17:63:24 | buffer10 | This 'scanf string argument' operation requires 12 bytes but the destination is only 10 bytes. |
|
||||
| tests.c:86:3:86:8 | call to strcpy | This 'call to strcpy' operation requires 6 bytes but the destination is only 5 bytes. |
|
||||
| tests.c:93:3:93:8 | call to strcpy | This 'call to strcpy' operation requires 6 bytes but the destination is only 5 bytes. |
|
||||
| tests.c:120:3:120:9 | call to sprintf | This 'call to sprintf' operation requires 17 bytes but the destination is only 1 bytes. |
|
||||
| tests.c:121:3:121:9 | call to sprintf | This 'call to sprintf' operation requires 17 bytes but the destination is only 16 bytes. |
|
||||
| tests.c:136:2:136:8 | call to sprintf | This 'call to sprintf' operation requires 11 bytes but the destination is only 10 bytes. |
|
||||
| unions.c:26:2:26:7 | call to strcpy | This 'call to strcpy' operation requires 21 bytes but the destination is only 16 bytes. |
|
||||
| unions.c:27:2:27:7 | call to strcpy | This 'call to strcpy' operation requires 21 bytes but the destination is only 16 bytes. |
|
||||
| unions.c:32:2:32:7 | call to strcpy | This 'call to strcpy' operation requires 31 bytes but the destination is only 25 bytes. |
|
||||
| var_size_struct.cpp:22:3:22:8 | call to strcpy | This 'call to strcpy' operation requires 10 bytes but the destination is only 9 bytes. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/OverrunWrite.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
| tests.c:103:3:103:9 | call to sprintf | This 'call to sprintf' operation may require 318 bytes because of float conversions, but the target is only 256 bytes. |
|
||||
| tests.c:105:3:105:9 | call to sprintf | This 'call to sprintf' operation may require 346 bytes because of float conversions, but the target is only 256 bytes. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/OverrunWriteFloat.ql
|
||||
@@ -0,0 +1,5 @@
|
||||
| tests.c:28:3:28:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
||||
| tests.c:29:3:29:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
||||
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
||||
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
|
||||
| tests.c:34:25:34:33 | buffer100 | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-120/UnboundedWrite.ql
|
||||
@@ -0,0 +1,171 @@
|
||||
//semmle-extractor-options: --edg --target --edg win64
|
||||
// Semmle test cases for UnboundedWrite.ql, BadlyBoundedWrite.ql, OverrunWrite.ql and OverrunWriteFloat.ql
|
||||
// Associated with CWE-120 http://cwe.mitre.org/data/definitions/120.html
|
||||
// Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long long size_t;
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
int scanf(const char *format, ...);
|
||||
int sscanf(const char *s, const char *format, ...);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Test cases for UnboundedWrite.ql
|
||||
{
|
||||
char buffer100[100];
|
||||
int i;
|
||||
|
||||
sprintf(buffer100, argv[0]); // BAD: argv[0] could be more than 100 characters
|
||||
sprintf(buffer100, "%s", argv[0]); // BAD: argv[0] could be more than 100 characters
|
||||
|
||||
scanf("%s", buffer100); // BAD: the input could be more than 100 characters
|
||||
scanf("%i", i); // GOOD: no problems with non-strings
|
||||
scanf("%i %s", i, buffer100); // BAD: second format parameter may overflow
|
||||
sscanf(argv[0], "%s", buffer100); // BAD: argv[0] could be more than 100 characters
|
||||
}
|
||||
|
||||
// Test cases for BadlyBoundedWrite.ql
|
||||
{
|
||||
char buffer110[110];
|
||||
|
||||
snprintf(buffer110, 109, argv[0]); // GOOD
|
||||
snprintf(buffer110, 110, argv[0]); // GOOD
|
||||
snprintf(buffer110, 111, argv[0]); // BAD: this could still overrun the 110 character buffer
|
||||
snprintf(buffer110, 109, "%s", argv[0]); // GOOD
|
||||
snprintf(buffer110, 110, "%s", argv[0]); // GOOD
|
||||
snprintf(buffer110, 111, "%s", argv[0]); // BAD: this could still overrun the 110 character buffer
|
||||
}
|
||||
|
||||
// Test cases for OverrunWrite.ql
|
||||
{
|
||||
char buffer10[10];
|
||||
|
||||
sprintf(buffer10, "123456789"); // GOOD
|
||||
sprintf(buffer10, "1234567890"); // BAD: the null terminator of this string overruns the buffer
|
||||
sprintf(buffer10, "%.9s", "123456789"); // GOOD
|
||||
sprintf(buffer10, "%.9s", "1234567890"); // GOOD
|
||||
sprintf(buffer10, "%.10s", "123456789"); // GOOD
|
||||
sprintf(buffer10, "%.10s", "1234567890"); // BAD: the precision specified is too large for this buffer
|
||||
|
||||
scanf("%8s", buffer10); // GOOD: restricted to 8 characters + null
|
||||
scanf("%9s", buffer10); // GOOD: restricted to 9 characters + null
|
||||
scanf("%10s", buffer10); // BAD: null can overflow
|
||||
scanf("%11s", buffer10); // BAD: string can overflow
|
||||
}
|
||||
|
||||
// More complex tests for OverrunWrite.ql
|
||||
{
|
||||
char buffer5[5];
|
||||
char *str4, *str24, *str35;
|
||||
|
||||
str4 = "1234";
|
||||
strcpy(buffer5, str4); // GOOD: str4 fits in the buffer
|
||||
|
||||
str24 = "12";
|
||||
if (argc == 1)
|
||||
{
|
||||
str24 = "1234";
|
||||
}
|
||||
strcpy(buffer5, str24); // GOOD: both possible strings fit in the buffer
|
||||
|
||||
str35 = "123";
|
||||
if (argc == 1)
|
||||
{
|
||||
str35 = "12345";
|
||||
}
|
||||
strcpy(buffer5, str35); // BAD: if str35 is "12345", it overflows the buffer
|
||||
|
||||
str35 = "abc";
|
||||
strcpy(buffer5, str35); // GOOD: str35 is guaranteed to fit now
|
||||
|
||||
strcpy(buffer5, (argc == 2) ? "1234" : "abcd"); // GOOD: both of the strings fit
|
||||
|
||||
strcpy(buffer5, (argc == 2) ? "1234" : "abcde"); // BAD: "abcde" overflows the buffer
|
||||
}
|
||||
|
||||
// Test cases for OverrunWriteFloat.ql
|
||||
{
|
||||
char buffer256[256];
|
||||
char buffer999[999];
|
||||
double bigval = 1e304;
|
||||
|
||||
sprintf(buffer256, "%e", bigval); // GOOD
|
||||
sprintf(buffer256, "%f", bigval); // BAD: this %f representation may need more than 256 characters
|
||||
sprintf(buffer256, "%g", bigval); // GOOD
|
||||
sprintf(buffer256, "%e%f%g", bigval, bigval, bigval); // BAD: the %f representation may need more than 256 characters
|
||||
|
||||
// GOOD: a 999 character buffer is sufficient in all of these cases
|
||||
sprintf(buffer999, "%e", bigval); // GOOD
|
||||
sprintf(buffer999, "%f", bigval); // GOOD
|
||||
sprintf(buffer999, "%g", bigval); // GOOD
|
||||
sprintf(buffer999, "%e%f%g", bigval, bigval, bigval); // GOOD
|
||||
}
|
||||
|
||||
// Test cases for %p
|
||||
{
|
||||
char buffer1[1];
|
||||
char buffer16[16];
|
||||
char buffer17[17];
|
||||
char buffer49[49];
|
||||
sprintf(buffer1, "%p", argv); // BAD
|
||||
sprintf(buffer16, "%p", argv); // BAD
|
||||
sprintf(buffer17, "%p", argv); // GOOD
|
||||
sprintf(buffer49, "%p and then a few more words", argv); // GOOD
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef char MyCharArray[10];
|
||||
|
||||
void test_fn2()
|
||||
{
|
||||
MyCharArray myBuffer10;
|
||||
|
||||
sprintf(myBuffer10, "%s", "123456789"); // GOOD
|
||||
sprintf(myBuffer10, "%s", "1234567890"); // BAD: buffer overflow
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *string;
|
||||
int value;
|
||||
} StringStruct;
|
||||
|
||||
#define GETSTRING(x) ((x)->string)
|
||||
#define GETVALUE(x) ((x)->value)
|
||||
#define MAX_INT_LEN (11)
|
||||
|
||||
void testStringStruct(StringStruct *ss)
|
||||
{
|
||||
snprintf(GETSTRING(ss), sizeof("Number: "), "Number: %i", GETVALUE(ss)); // BAD: potential buffer overflow [NOT DETECTED]
|
||||
snprintf(GETSTRING(ss), sizeof("Number: ") + MAX_INT_LEN, "Number: %i", GETVALUE(ss)); // GOOD
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int size;
|
||||
char data[0];
|
||||
} VarSizeStruct;
|
||||
|
||||
void testVarSizeStruct()
|
||||
{
|
||||
char buffer[sizeof(VarSizeStruct) + 10];
|
||||
VarSizeStruct *s = buffer;
|
||||
|
||||
snprintf(s->data, 10, "abcdefghijklmnopqrstuvwxyz"); // GOOD
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//semmle-extractor-options: --edg --target --edg win64
|
||||
// Semmle test cases for UnboundedWrite.ql, BadlyBoundedWrite.ql, OverrunWrite.ql and OverrunWriteFloat.ql
|
||||
// Associated with CWE-120 http://cwe.mitre.org/data/definitions/120.html
|
||||
// Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long long size_t;
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
typedef union {
|
||||
char *ptr;
|
||||
char buffer[15];
|
||||
} MyUnion;
|
||||
|
||||
void unions_test(MyUnion *mu)
|
||||
{
|
||||
char buffer[25];
|
||||
|
||||
strcpy(mu, "1234567890"); // GOOD
|
||||
strcpy(&(mu->ptr), "1234567890"); // GOOD (dubious)
|
||||
strcpy(&(mu->buffer), "1234567890"); // GOOD
|
||||
strcpy(mu, "12345678901234567890"); // BAD [NOT DETECTED]
|
||||
strcpy(&(mu->ptr), "12345678901234567890"); // BAD
|
||||
strcpy(&(mu->buffer), "12345678901234567890"); // BAD
|
||||
|
||||
mu->ptr = buffer;
|
||||
strcpy(mu->ptr, "1234567890"); // GOOD
|
||||
strcpy(mu->ptr, "12345678901234567890"); // GOOD
|
||||
strcpy(mu->ptr, "123456789012345678901234567890"); // BAD
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//semmle-extractor-options: --edg --target --edg linux_x86_64
|
||||
|
||||
// Semmle test cases for rule CWE-120 involving variable size structs.
|
||||
|
||||
// library types, functions etc
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
// --- Semmle tests ---
|
||||
|
||||
struct varStruct {
|
||||
int size;
|
||||
char data[1];
|
||||
};
|
||||
|
||||
void testVarStruct() {
|
||||
varStruct *vs = (varStruct *)malloc(sizeof(varStruct) + 8);
|
||||
|
||||
vs->size = 9;
|
||||
strcpy(vs->data, "12345678"); // GOOD
|
||||
strcpy(vs->data, "123456789"); // BAD: buffer overflow
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
| more_tests.cpp:23:2:23:12 | call to myFunction2 | Calls to $@ should use the value -1 as a terminator (4 calls do). | more_tests.cpp:5:6:5:16 | myFunction2 | myFunction2 |
|
||||
| more_tests.cpp:34:2:34:12 | call to myFunction4 | Calls to $@ should use the value 0 as a terminator (3 calls do). | more_tests.cpp:7:6:7:16 | myFunction4 | myFunction4 |
|
||||
| more_tests.cpp:44:2:44:12 | call to myFunction6 | Calls to $@ should use the value 0 as a terminator (3 calls do). | more_tests.cpp:9:6:9:16 | myFunction6 | myFunction6 |
|
||||
| more_tests.cpp:55:2:55:12 | call to myFunction7 | Calls to $@ should use the value 0 as a terminator (7 calls do). | more_tests.cpp:10:6:10:16 | myFunction7 | myFunction7 |
|
||||
| more_tests.cpp:56:2:56:12 | call to myFunction7 | Calls to $@ should use the value 0 as a terminator (7 calls do). | more_tests.cpp:10:6:10:16 | myFunction7 | myFunction7 |
|
||||
| tests.c:34:2:34:3 | call to f1 | Calls to $@ should use the value 0 as a terminator (4 calls do). | tests.c:4:6:4:7 | f1 | f1 |
|
||||
| tests.c:67:2:67:3 | call to f6 | Calls to $@ should use the value -1 as a terminator (3 calls do). | tests.c:24:6:24:7 | f6 | f6 |
|
||||
| tests.c:68:2:68:3 | call to f6 | Calls to $@ should use the value -1 as a terminator (3 calls do). | tests.c:24:6:24:7 | f6 | f6 |
|
||||
| tests.c:73:2:73:3 | call to f7 | Calls to $@ should use the value 0 as a terminator (3 calls do). | tests.c:28:6:28:7 | f7 | f7 |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-121/UnterminatedVarargsCall.ql
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
#define NULL (0)
|
||||
|
||||
void myFunction1(char *format, ...);
|
||||
void myFunction2(...);
|
||||
void myFunction3(...);
|
||||
void myFunction4(int i, int j, ...);
|
||||
void myFunction5(...);
|
||||
void myFunction6(...);
|
||||
void myFunction7(...);
|
||||
|
||||
int main()
|
||||
{
|
||||
int x;
|
||||
|
||||
myFunction1("%i", 0); // not common enough to be assumed a terminator
|
||||
myFunction1("%i", x);
|
||||
|
||||
myFunction2(-1);
|
||||
myFunction2(0, -1);
|
||||
myFunction2(0, 1, -1);
|
||||
myFunction2(0, 1, 2, -1);
|
||||
myFunction2(0, 1, 2, 3); // missing terminator
|
||||
|
||||
myFunction3(-1);
|
||||
myFunction3(0, -1);
|
||||
myFunction3(-1, 1, -1); // -1 isn't a terminator because it's used in a non-terminal position
|
||||
myFunction3(0, 1, 2, -1);
|
||||
myFunction3(0, 1, 2, 3);
|
||||
|
||||
myFunction4(x, x, 0);
|
||||
myFunction4(0, x, 1, 0);
|
||||
myFunction4(0, 0, 1, 1, 0);
|
||||
myFunction4(x, 0, 1, 1, 1); // missing terminator
|
||||
|
||||
myFunction5('a', 'b', 'c', 0); // ambiguous terminator
|
||||
myFunction5('a', 'b', 'c', 0);
|
||||
myFunction5('a', 'b', 'c', 0);
|
||||
myFunction5('a', 'b', 'c', -1);
|
||||
myFunction5('a', 'b', 'c', -1);
|
||||
myFunction5('a', 'b', 'c', -1);
|
||||
|
||||
myFunction6(0.0);
|
||||
myFunction6(1.0); // missing terminator
|
||||
myFunction6(1.0, 2.0, 0.0);
|
||||
myFunction6(1.0, 2.0, 3.0, 0.0);
|
||||
|
||||
myFunction7(NULL);
|
||||
myFunction7("hello", "world", NULL);
|
||||
myFunction7("apple", "banana", "pear", "mango", NULL);
|
||||
myFunction7("dog", "cat", "elephant", "badger", "fish", NULL);
|
||||
myFunction7("one", "two", "three", 0);
|
||||
myFunction7("alpha", "beta", "gamma", 0);
|
||||
myFunction7("", 0);
|
||||
myFunction7("yes", "no"); // missing terminator
|
||||
myFunction7(); // missing terminator
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Semmle test cases for UnterminatedVarargsCall.ql
|
||||
// Associated with CWE-121 http://cwe.mitre.org/data/definitions/121.html
|
||||
|
||||
void f1(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f2(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f3(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f4(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f5(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f6(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void f7(char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
f1("", 1); // BAD: not terminated with 0
|
||||
f1("", 1, 0);
|
||||
f1("", 1, 1, 0);
|
||||
f1("", 1, 1, 1, 0);
|
||||
f1("", 1, 1, 1, 1, 0);
|
||||
|
||||
// GOOD: no obvious required terminator
|
||||
f2("", 10);
|
||||
|
||||
// GOOD: 0 is not common enough to be sure it's a terminator
|
||||
f3("", 0);
|
||||
f3("", 10);
|
||||
|
||||
// GOOD: -1 is not common enough to be sure it's a terminator
|
||||
f4("", 0);
|
||||
f4("", 0);
|
||||
f4("", -1);
|
||||
f4("", -1);
|
||||
f4("", -1);
|
||||
f4("", 1);
|
||||
|
||||
// GOOD: no obvious required terminator
|
||||
f5("");
|
||||
f5("");
|
||||
f5("");
|
||||
f5("");
|
||||
f5("", 0);
|
||||
f5("", 0);
|
||||
f5("", 10);
|
||||
|
||||
f6("fsdf", 3, 8, -1);
|
||||
f6("a", 7, 9, 10, -1);
|
||||
f6("a", 1, 22, 6, 17, 2, -1);
|
||||
f6("fgasfgas", 5, 6, argc); // BAD: not (necessarily) terminated with -1
|
||||
f6("sadfsaf"); // BAD: not terminated with -1
|
||||
|
||||
f7("", 0);
|
||||
f7("", 0);
|
||||
f7("", 0);
|
||||
f7(""); // BAD: not terminated with 0
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
| test1.c:18:16:18:16 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
|
||||
| test1.c:33:11:33:11 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
|
||||
| test1.c:37:11:37:11 | i | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
|
||||
| test1.c:53:15:53:15 | j | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | test1.c:8:16:8:19 | argv | User-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-129/ImproperArrayIndexValidation.ql
|
||||
@@ -0,0 +1,54 @@
|
||||
int atoi(const char *nptr);
|
||||
|
||||
void dosomething(char c);
|
||||
|
||||
const char chr[26] = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i = atoi(argv[1]);
|
||||
test1(i);
|
||||
test2(i);
|
||||
test3(i);
|
||||
test4(i);
|
||||
test5(i);
|
||||
}
|
||||
|
||||
void test1(int i) {
|
||||
// BAD: i has not been validated.
|
||||
char c = chr[i];
|
||||
dosomething(c);
|
||||
}
|
||||
|
||||
void test2(int i) {
|
||||
if (0 <= i && i < 26) {
|
||||
// GOOD: i has been validated.
|
||||
char c = chr[i];
|
||||
dosomething(c);
|
||||
}
|
||||
}
|
||||
|
||||
int myArray[10];
|
||||
|
||||
void test3(int i) {
|
||||
myArray[i] = 0; // BAD: i has not been validated
|
||||
|
||||
i = 5;
|
||||
|
||||
myArray[i] = 0; // GOOD: i is not untrusted input [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test4(int i) {
|
||||
myArray[i] = 0; // BAD: i has not been validated [NOT REPORTED]
|
||||
|
||||
if ((i < 0) || (i >= 10)) return;
|
||||
|
||||
myArray[i] = 1; // GOOD: i has been validated
|
||||
}
|
||||
|
||||
void test5(int i) {
|
||||
int j = 0;
|
||||
|
||||
j = i;
|
||||
|
||||
j = myArray[j]; // BAD: j has not been validated
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Semmle test case for NoSpaceForZeroTerminator.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void bad0(char *str) {
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
char *buffer = malloc(strlen(str));
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void good0(char *str) {
|
||||
// GOOD -- Allocating extra byte for terminator
|
||||
char *buffer = malloc(strlen(str)+1);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
void bad1(char *str) {
|
||||
int len = strlen(str);
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
char *buffer = malloc(len);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void good1(char *str) {
|
||||
int len = strlen(str);
|
||||
// GOOD -- Allocating extra byte for terminator
|
||||
char *buffer = malloc(len+1);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
|
||||
void bad2(char *str) {
|
||||
int len = strlen(str);
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
char *buffer = malloc(len);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void good2(char *str) {
|
||||
int len = strlen(str)+1;
|
||||
// GOOD -- Allocating extra byte for terminator
|
||||
char *buffer = malloc(len);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void bad3(char *str) {
|
||||
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
|
||||
char *buffer = malloc(strlen(str) * sizeof(char));
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void good3(char *str) {
|
||||
// GOOD -- Allocating extra byte for terminator
|
||||
char *buffer = malloc((strlen(str) + 1) * sizeof(char));
|
||||
free(buffer);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Semmle test case for NoSpaceForZeroTerminator.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
size_t wcslen(const wchar_t *s);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void bad1(wchar_t *wstr) {
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr));
|
||||
free(wbuffer);
|
||||
}
|
||||
|
||||
void bad2(wchar_t *wstr) {
|
||||
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
|
||||
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t));
|
||||
free(wbuffer);
|
||||
}
|
||||
|
||||
void good1(wchar_t *wstr) {
|
||||
// GOOD -- Allocating extra character for terminator
|
||||
wchar_t *wbuffer = (wchar_t *)malloc((wcslen(wstr) + 1) * sizeof(wchar_t));
|
||||
free(wbuffer);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.c:15:20:15:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.c:29:20:29:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.c:44:20:44:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:18:35:18:40 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Semmle test case for SizeCheck.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void bad0(void) {
|
||||
|
||||
float *fptr = malloc(3); // BAD -- Too small
|
||||
double *dptr = malloc(5); // BAD -- Too small
|
||||
free(fptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void good0(void) {
|
||||
|
||||
float *fptr = malloc(4); // GOOD -- Correct size
|
||||
double *dptr = malloc(8); // GOOD -- Correct size
|
||||
free(fptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void bad1(void) {
|
||||
|
||||
float *fptr = malloc(sizeof(short)); // BAD -- Too small
|
||||
double *dptr = malloc(sizeof(float)); // BAD -- Too small
|
||||
free(fptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void good1(void) {
|
||||
|
||||
float *fptr = malloc(sizeof(float)); // GOOD -- Correct size
|
||||
double *dptr = malloc(sizeof(double)); // GOOD -- Correct size
|
||||
free(fptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.c:16:19:16:24 | call to malloc | Type 'float' is 4 bytes, but only 3 bytes are allocated. |
|
||||
| test.c:17:20:17:25 | call to malloc | Type 'double' is 8 bytes, but only 5 bytes are allocated. |
|
||||
| test.c:32:19:32:24 | call to malloc | Type 'float' is 4 bytes, but only 2 bytes are allocated. |
|
||||
| test.c:33:20:33:25 | call to malloc | Type 'double' is 8 bytes, but only 4 bytes are allocated. |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/SizeCheck.ql
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Semmle test case for SizeCheck2.ql
|
||||
Associated with CWE-131 http://cwe.mitre.org/data/definitions/131.html
|
||||
Each query is expected to find exactly the lines marked BAD in the section corresponding to it.
|
||||
*/
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
void bad0(void) {
|
||||
|
||||
long long *lptr = malloc(27); // BAD -- Not a multiple of sizeof(long long)
|
||||
double *dptr = malloc(33); // BAD -- Not a multiple of sizeof(double)
|
||||
free(lptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void good0(void) {
|
||||
|
||||
float *fptr = malloc(24); // GOOD -- An integral multiple
|
||||
double *dptr = malloc(56); // GOOD -- An integral multiple
|
||||
free(fptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void bad1(void) {
|
||||
|
||||
long long *lptr = malloc(sizeof(long long)*7/2); // BAD -- Not a multiple of sizeof(long long)
|
||||
double *dptr = malloc(sizeof(double)*5/2); // BAD -- Not a multiple of sizeof(double)
|
||||
free(lptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
void good1(void) {
|
||||
|
||||
long long *lptr = malloc(sizeof(long long)*5); // GOOD -- An integral multiple
|
||||
double *dptr = malloc(sizeof(double)*7); // GOOD -- An integral multiple
|
||||
free(lptr);
|
||||
free(dptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.c:16:23:16:28 | call to malloc | Allocated memory (27 bytes) is not a multiple of the size of 'long long' (8 bytes). |
|
||||
| test.c:17:20:17:25 | call to malloc | Allocated memory (33 bytes) is not a multiple of the size of 'double' (8 bytes). |
|
||||
| test.c:32:23:32:28 | call to malloc | Allocated memory (28 bytes) is not a multiple of the size of 'long long' (8 bytes). |
|
||||
| test.c:33:20:33:25 | call to malloc | Allocated memory (20 bytes) is not a multiple of the size of 'double' (8 bytes). |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/SizeCheck2.ql
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user