Merge branch 'main' into post-release-prep/codeql-cli-2.7.5

This commit is contained in:
Andrew Eisenberg
2022-01-14 08:23:43 -08:00
744 changed files with 39341 additions and 19067 deletions

View File

@@ -26,6 +26,8 @@ where
// At least for C programs on Windows, BOOL is a common typedef for a type
// representing BoolType.
not bf.getType().hasName("BOOL") and
// GLib's gboolean is a typedef for a type representing BoolType.
not bf.getType().hasName("gboolean") and
// If this is true, then there cannot be unsigned sign extension or overflow.
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
not bf.isAnonymous() and

View File

@@ -18,8 +18,5 @@
<p>To fix the problem, either the second argument to <code>snprintf</code> should be changed to 80, or the buffer extended to 256 characters. A further improvement is to use a preprocessor define so that the size is only specified in one place, potentially preventing future recurrence of this issue.</p>
</example>
<references>
</references>
<include src="OverrunWriteReferences.inc.qhelp" />
</qhelp>

View File

@@ -1,9 +1,9 @@
void sayHello()
void sayHello(uint32_t userId)
{
char buffer[10];
char buffer[18];
// BAD: this message overflows the buffer
strcpy(buffer, "Hello, world!");
// BAD: this message overflows the buffer if userId >= 10000
sprintf(buffer, "Hello, user %d!", userId);
MessageBox(hWnd, buffer, "New Message", MB_OK);
}

View File

@@ -6,30 +6,20 @@
<p>The program performs a buffer copy or write operation with no upper limit on the size of the copy, and it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.</p>
</overview>
<recommendation>
<p>Always control the length of buffer copy and buffer write operations. <code>strncpy</code> should be used over <code>strcpy</code>, <code>snprintf</code> over <code>sprintf</code>, and in other cases 'n-variant' functions should be preferred.</p>
</recommendation>
<include src="OverrunWriteRecommendation.inc.qhelp" />
<example>
<sample src="OverrunWrite.c" />
<p>In this example, the call to <code>strcpy</code> copies a message of 14 characters (including the terminating null) into a buffer with space for just 10 characters. As such, the last four characters overflow the buffer resulting in undefined behavior.</p>
<p>In this example, the call to <code>sprintf</code> writes a message of 14 characters (including the terminating null) plus the length of the string conversion of `userId` into a buffer with space for just 18 characters. As such, if `userId` is greater or equal to `10000`, the last characters overflow the buffer resulting in undefined behavior.</p>
<p>To fix this issue three changes should be made:</p>
<p>To fix this issue these changes should be made:</p>
<ul>
<li>Control the size of the buffer using a preprocessor define.</li>
<li>Replace the call to <code>strcpy</code> with <code>strncpy</code>, specifying the define as the maximum length to copy. This will prevent the buffer overflow.</li>
<li>Consider increasing the buffer size, say to 20 characters, so that the message is displayed correctly.</li>
<li>Control the size of the buffer by declaring it with a compile time constant.</li>
<li>Preferably, replace the call to <code>sprintf</code> with <code>snprintf</code>, using the defined constant size of the buffer or `sizeof(buffer)` as maximum length to write. This will prevent the buffer overflow.</li>
<li>Optionally, if `userId` is expected to be less than `10000`, then return or throw an error if `userId` is out of bounds.</li>
<li>Otherwise, consider increasing the buffer size to at least 25 characters, so that the message is displayed correctly regardless of the value of `userId`.</li>
</ul>
</example>
<references>
<li>CERT C Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.</li>
<!-- LocalWords: preprocessor CWE STR
-->
</references>
<include src="OverrunWriteReferences.inc.qhelp" />
</qhelp>

View File

@@ -21,12 +21,14 @@ import semmle.code.cpp.commons.Alloc
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
*/
from BufferWrite bw, Expr dest, int destSize, int estimated
from BufferWrite bw, Expr dest, int destSize, int estimated, BufferWriteEstimationReason reason
where
not bw.hasExplicitLimit() and // has no explicit size limit
dest = bw.getDest() and
destSize = getBufferSize(dest, _) and
estimated = bw.getMaxDataLimited(_) and
estimated = bw.getMaxDataLimited(reason) and
// we exclude ValueFlowAnalysis as it is reported in cpp/very-likely-overruning-write
not reason instanceof ValueFlowAnalysis and
// we can deduce that too much data may be copied (even without
// long '%f' conversions)
estimated > destSize

View File

@@ -6,30 +6,19 @@
<p>The program performs a buffer copy or write operation that includes one or more float to string conversions (i.e. the %f format specifier), which may overflow the destination buffer if extreme inputs are given. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.</p>
</overview>
<recommendation>
<p>Always control the length of buffer copy and buffer write operations. <code>strncpy</code> should be used over <code>strcpy</code>, <code>snprintf</code> over <code>sprintf</code>, and in other cases 'n-variant' functions should be preferred.</p>
</recommendation>
<include src="OverrunWriteRecommendation.inc.qhelp" />
<example>
<sample src="OverrunWriteFloat.c" />
<p>In this example, the call to <code>sprintf</code> contains a %f format specifier. Though a 256 character buffer has been allowed, it is not sufficient for the most extreme floating point inputs. For example the representation of double value 1e304 (that is 1 with 304 zeroes after it) will overflow a buffer of this length.</p>
<p>In this example, the call to <code>sprintf</code> contains a <code>%f</code> format specifier. Though a 256 character buffer has been allowed, it is not sufficient for the most extreme floating point inputs. For example the representation of double value 1e304 (that is 1 with 304 zeroes after it) will overflow a buffer of this length.</p>
<p>To fix this issue three changes should be made:</p>
<ul>
<li>Control the size of the buffer using a preprocessor define.</li>
<li>Replace the call to <code>sprintf</code> with <code>snprintf</code>, specifying the define as the maximum length to copy. This will prevent the buffer overflow.</li>
<li>Consider using the %g format specifier instead of %f.</li>
<li>Consider using the <code>%g</code> format specifier instead of <code>%f</code>.</li>
</ul>
</example>
<references>
<li>CERT C Coding
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee
that storage for strings has sufficient space for character data and
the null terminator</a>.</li>
</references>
<include src="OverrunWriteReferences.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,8 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<recommendation>
<p>Always control the length of buffer copy and buffer write operations. <code>strncpy</code> should be used over <code>strcpy</code>, <code>snprintf</code> over <code>sprintf</code>, and in other cases 'n-variant' functions should be preferred.</p>
</recommendation>
</qhelp>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<references>
<li>CERT C Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.</li>
<li>CERT C++ Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.</li>
<!-- LocalWords: preprocessor CWE STR
-->
</references>
</qhelp>

View File

@@ -6,10 +6,7 @@
<p>The program performs a buffer copy or write operation with no upper limit on the size of the copy. An unexpectedly long input that reaches this code will cause the buffer to overflow. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.</p>
</overview>
<recommendation>
<p>Always control the length of buffer copy and buffer write operations. <code>strncpy</code> should be used over <code>strcpy</code>, <code>snprintf</code> over <code>sprintf</code> etc. In general 'n-variant' functions should be preferred.</p>
</recommendation>
<include src="OverrunWriteRecommendation.inc.qhelp" />
<example>
<sample src="UnboundedWrite.c" />
@@ -18,13 +15,5 @@
<p>To fix the problem the call to <code>sprintf</code> should be replaced with <code>snprintf</code>, specifying a maximum length of 80 characters.</p>
</example>
<references>
<li>CERT C++ Coding Standard: <a href="https://www.securecoding.cert.org/confluence/display/cplusplus/STR50-CPP.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR50-CPP. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.</li>
<!-- LocalWords: CWE STR
-->
</references>
<include src="OverrunWriteReferences.inc.qhelp" />
</qhelp>

View File

@@ -33,7 +33,7 @@ import TaintedWithPath
* hasExplicitLimit() exists(getMaxData()) exists(getBufferSize(bw.getDest(), _))) handled by
* NO NO either UnboundedWrite.ql isUnboundedWrite()
* NO YES NO UnboundedWrite.ql isMaybeUnboundedWrite()
* NO YES YES OverrunWrite.ql, OverrunWriteFloat.ql
* NO YES YES VeryLikelyOverrunWrite.ql, OverrunWrite.ql, OverrunWriteFloat.ql
* YES either YES BadlyBoundedWrite.ql
* YES either NO (assumed OK)
*/

View File

@@ -0,0 +1,14 @@
int sayHello(uint32_t userId)
{
char buffer[17];
if (userId > 9999) return USER_ID_OUT_OF_BOUNDS;
// BAD: this message overflows the buffer if userId >= 1000,
// as no space for the null terminator was accounted for
sprintf(buffer, "Hello, user %d!", userId);
MessageBox(hWnd, buffer, "New Message", MB_OK);
return SUCCESS;
}

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The program performs a buffer copy or write operation with no upper limit on the size of the copy. By analyzing the bounds of the expressions involved, it appears that certain inputs will cause a buffer overflow to occur in this case. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.</p>
</overview>
<include src="OverrunWriteRecommendation.inc.qhelp" />
<example>
<sample src="VeryLikelyOverrunWrite.c" />
<p>In this example, the call to <code>sprintf</code> writes a message of 14 characters (including the terminating null) plus the length of the string conversion of `userId` into a buffer with space for just 17 characters. While `userId` is checked to occupy no more than 4 characters when converted, there is no space in the buffer for the terminating null character if `userId >= 1000`. In this case, the null character overflows the buffer resulting in undefined behavior.</p>
<p>To fix this issue these changes should be made:</p>
<ul>
<li>Control the size of the buffer by declaring it with a compile time constant.</li>
<li>Preferably, replace the call to <code>sprintf</code> with <code>snprintf</code>, using the defined constant size of the buffer or `sizeof(buffer)` as maximum length to write. This will prevent the buffer overflow.</li>
<li>Increasing the buffer size to account for the full range of `userId` and the terminating null character.</li>
</ul>
</example>
<include src="OverrunWriteReferences.inc.qhelp" />
</qhelp>

View File

@@ -0,0 +1,34 @@
/**
* @name Likely overrunning write
* @description Buffer write operations that do not control the length
* of data written may overflow
* @kind problem
* @problem.severity error
* @security-severity 9.3
* @precision high
* @id cpp/very-likely-overrunning-write
* @tags reliability
* security
* external/cwe/cwe-120
* external/cwe/cwe-787
* external/cwe/cwe-805
*/
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.commons.Alloc
/*
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
*/
from BufferWrite bw, Expr dest, int destSize, int estimated, ValueFlowAnalysis reason
where
not bw.hasExplicitLimit() and // has no explicit size limit
dest = bw.getDest() and
destSize = getBufferSize(dest, _) and
estimated = bw.getMaxDataLimited(reason) and
// we can deduce from non-trivial range analysis that too much data may be copied
estimated > destSize
select bw,
"This '" + bw.getBWDesc() + "' operation requires " + estimated +
" bytes but the destination is only " + destSize + " bytes."

View File

@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity warning
* @security-severity 8.6
* @precision medium
* @precision high
* @id cpp/uncontrolled-arithmetic
* @tags security
* external/cwe/cwe-190
@@ -82,8 +82,11 @@ predicate missingGuard(VariableAccess va, string effect) {
op.getUnspecifiedType().(IntegralType).isUnsigned() and
not op instanceof MulExpr
or
// overflow
missingGuardAgainstOverflow(op, va) and effect = "overflow"
// overflow - only report signed integer overflow since unsigned overflow
// is well-defined.
op.getUnspecifiedType().(IntegralType).isSigned() and
missingGuardAgainstOverflow(op, va) and
effect = "overflow"
)
}

View File

@@ -89,45 +89,83 @@ predicate referenceTo(Expr source, Expr use) {
)
}
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
where
// `check` looks like a check on a filename
(
(
// either:
// an access check
check = accessCheck(checkPath)
or
// a stat
check = stat(checkPath, _)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Expr call, Expr e, Variable v |
call = stat(checkPath, e) and
e.getAChild*().(VariableAccess).getTarget() = v and
check.(VariableAccess).getTarget() = v and
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
)
) and
// `op` looks like an operation on a filename
use = filenameOperation(usePath)
or
// another filename operation (null pointers can indicate errors)
check = filenameOperation(checkPath) and
// `op` looks like a sensitive operation on a filename
use = sensitiveFilenameOperation(usePath)
) and
// `checkPath` and `usePath` refer to the same SSA variable
exists(SsaDefinition def, StackVariable v |
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
) and
// the return value of `check` is used (possibly with one step of
// variable indirection) in a guard which controls `use`
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
pragma[noinline]
predicate statCallWithPointer(Expr checkPath, Expr call, Expr e, Variable v) {
call = stat(checkPath, e) and
e.getAChild*().(VariableAccess).getTarget() = v
}
predicate checksPath(Expr check, Expr checkPath) {
// either:
// an access check
check = accessCheck(checkPath)
or
// a stat
check = stat(checkPath, _)
or
// access to a member variable on the stat buf
// (morally, this should be a use-use pair, but it seems unlikely
// that this variable will get reused in practice)
exists(Expr call, Expr e, Variable v |
statCallWithPointer(checkPath, call, e, v) and
check.(VariableAccess).getTarget() = v and
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
)
}
pragma[nomagic]
predicate checkPathControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
checksPath(pragma[only_bind_into](check), checkPath)
}
pragma[nomagic]
predicate fileNameOperationControlsUse(Expr check, Expr checkPath, Expr use) {
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
guard.controls(use.getBasicBlock(), _)
) and
pragma[only_bind_into](check) = filenameOperation(checkPath)
}
predicate checkUse(Expr check, Expr checkPath, FunctionCall use, Expr usePath) {
// `check` is part of a guard that controls `use`
checkPathControlsUse(check, checkPath, use) and
// `check` looks like a check on a filename
checksPath(check, checkPath) and
// `op` looks like an operation on a filename
use = filenameOperation(usePath)
or
// `check` is part of a guard that controls `use`
fileNameOperationControlsUse(check, checkPath, use) and
// another filename operation (null pointers can indicate errors)
check = filenameOperation(checkPath) and
// `op` looks like a sensitive operation on a filename
use = sensitiveFilenameOperation(usePath)
}
pragma[noinline]
predicate isCheckedPath(
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
) {
checkUse(check, checkPath, use, usePath) and
def.getAUse(v) = checkPath
}
pragma[noinline]
predicate isUsedPath(
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
) {
checkUse(check, checkPath, use, usePath) and
def.getAUse(v) = usePath
}
from Expr check, Expr checkPath, FunctionCall use, Expr usePath, SsaDefinition def, StackVariable v
where
// `checkPath` and `usePath` refer to the same SSA variable
isCheckedPath(check, def, v, use, usePath, checkPath) and
isUsedPath(check, def, v, use, usePath, checkPath)
select use,
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
usePath, "filename", check, "checked"

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* A new `cpp/very-likely-overruning-write` query has been added to the default query suite for C/C++. The query reports some results that were formerly flagged by `cpp/overruning-write`.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Added exception for GLib's gboolean to cpp/ambiguously-signed-bit-field.
This change reduces the number of false positives in the query.

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* The "Uncontrolled data in arithmetic expression" (cpp/uncontrolled-arithmetic) query has been enhanced to reduce false positive results and its @precision increased to high.