Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if(strcpy(szbuf1, "Manager") == 0) // most likely strcmp was intended instead of strcpy
{
// ...
}
43 changes: 43 additions & 0 deletions cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>This rule finds uses of the string copy function calls that return the <code>destination</code> parameter,
and that do not have a return value reserved to indicate an error.</p>

<p>The rule flags occurrences using such string copy functions as the conditional of an <code>if</code> statement, either directly, as part of an equality operator or a logical operator.</p>

<p>The string copy functions that the rule takes into consideration are: </p>
<ul>
<li>strcpy</li>
<li>wcscpy</li>
<li>_mbscpy</li>
<li>strncpy</li>
<li>_strncpy_l</li>
<li>wcsncpy</li>
<li>_wcsncpy_l</li>
<li>_mbsncpy</li>
<li>_mbsncpy_l</li>
</ul>

<p>NOTE: It is highly recommended to consider using a more secure version of string manipulation functions suchas as <code>strcpy_s</code>.</p>

</overview>
<recommendation>
<p>Check to ensure that the flagged expressions are not typos.</p>
<p>If a string comparison is intended, change the function to the appropriate string comparison function.</p>
<p>If a string copy is really intended, very likely a secure version of the string copy function such as <code>strcpy_s</code> was intended instead of the insecure version of the string copy function.</p>

</recommendation>
<example><sample src="UsingStrcpyAsBoolean.cpp" />
</example>

<references>
<li>Microsoft Books on Line: <a href="https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/ccf4h9w8(v=vs.110)">C6324</a></li>
<li>Microsoft Books on Line: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strcpy-wcscpy-mbscpy?view=vs-2017">strcpy, wcscpy, _mbscpy</a></li>
<li>US-CERT: <a href="https://www.us-cert.gov/bsi/articles/knowledge/coding-practices/strcpy_s-and-strcat_s">strncpy_s() and strncat_s()</a></li>

</references>
</qhelp>
77 changes: 77 additions & 0 deletions cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @name Using the return value of a strcpy or related string copy function as a boolean operator
* @description The return value for strcpy, strncpy, or related string copy functions have no reserved return value to indicate an error.
* Using the return values of these functions as boolean function .
* Either the intent was to use a more secure version of a string copy function (such as strcpy_s), or a string compare function (such as strcmp).
* @kind problem
* @problem.severity error
* @precision high
* @id cpp/string-copy-return-value-as-boolean
* @tags external/microsoft/C6324
*/

import cpp
import semmle.code.cpp.dataflow.DataFlow

predicate isStringComparisonFunction(string functionName) {
functionName = "strcpy"
or functionName = "wcscpy"
or functionName = "_mbscpy"
or functionName = "strncpy"
or functionName = "_strncpy_l"
or functionName = "wcsncpy"
or functionName = "_wcsncpy_l"
or functionName = "_mbsncpy"
or functionName = "_mbsncpy_l"
}

predicate isBoolean( Expr e1 )
{
exists ( Type t1 |
t1 = e1.getType() and
(t1.hasName("bool") or t1.hasName("BOOL") or t1.hasName("_Bool"))
)
}

predicate isStringCopyCastedAsBoolean( FunctionCall func, Expr expr1, string msg ) {
DataFlow::localFlow(DataFlow::exprNode(func), DataFlow::exprNode(expr1))
and isBoolean( expr1.getConversion*())
and isStringComparisonFunction( func.getTarget().getQualifiedName())
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used as boolean."
}

predicate isStringCopyUsedInLogicalOperationOrCondition( FunctionCall func, Expr expr1, string msg ) {
isStringComparisonFunction( func.getTarget().getQualifiedName() )
and (((
// it is being used in an equality or logical operation
exists( EqualityOperation eop |
eop = expr1
and func = eop.getAChild()
)
or exists( UnaryLogicalOperation ule |
expr1 = ule
and func = ule.getAChild()
)
or exists( BinaryLogicalOperation ble |
expr1 = ble
and func = ble.getAChild()
)
)
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used in a logical operation."
)
or
exists( ConditionalStmt condstmt |
condstmt.getAChild() = expr1 |
// or the string copy function is used directly as the conditional expression
func = condstmt.getChild(0)
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used directly in a conditional expression."
))
}

from FunctionCall func, Expr expr1, string msg
where
( isStringCopyCastedAsBoolean(func, expr1, msg) and
not isStringCopyUsedInLogicalOperationOrCondition(func, expr1, _)
)
or isStringCopyUsedInLogicalOperationOrCondition(func, expr1, msg)
select expr1, msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
| test.c:34:9:34:14 | call to strcpy | Return Value of strcpy used directly in a conditional expression. |
| test.c:38:9:38:31 | ! ... | Return Value of strcpy used in a logical operation. |
| test.c:42:9:42:35 | ... == ... | Return Value of strcpy used in a logical operation. |
| test.c:46:9:46:48 | ... && ... | Return Value of strcpy used in a logical operation. |
| test.c:50:9:50:15 | call to strncpy | Return Value of strncpy used directly in a conditional expression. |
| test.c:54:6:54:34 | ! ... | Return Value of strncpy used in a logical operation. |
| test.c:58:11:58:39 | ! ... | Return Value of strncpy used in a logical operation. |
| test.c:60:11:60:37 | ... && ... | Return Value of strcpy used in a logical operation. |
| test.c:62:11:62:37 | ... == ... | Return Value of strcpy used in a logical operation. |
| test.c:64:11:64:37 | ... != ... | Return Value of strcpy used in a logical operation. |
| test.cpp:75:9:75:14 | call to strcpy | Return Value of strcpy used directly in a conditional expression. |
| test.cpp:79:9:79:31 | ! ... | Return Value of strcpy used in a logical operation. |
| test.cpp:79:10:79:15 | call to strcpy | Return Value of strcpy used as boolean. |
| test.cpp:83:9:83:35 | ... == ... | Return Value of strcpy used in a logical operation. |
| test.cpp:87:9:87:48 | ... && ... | Return Value of strcpy used in a logical operation. |
| test.cpp:87:27:87:32 | call to strcpy | Return Value of strcpy used as boolean. |
| test.cpp:91:9:91:37 | call to wcscpy | Return Value of wcscpy used directly in a conditional expression. |
| test.cpp:95:9:95:14 | call to wcscpy | Return Value of wcscpy used directly in a conditional expression. |
| test.cpp:99:9:99:15 | call to _mbscpy | Return Value of _mbscpy used directly in a conditional expression. |
| test.cpp:103:9:103:15 | call to strncpy | Return Value of strncpy used directly in a conditional expression. |
| test.cpp:107:9:107:15 | call to wcsncpy | Return Value of wcsncpy used directly in a conditional expression. |
| test.cpp:111:9:111:16 | call to _mbsncpy | Return Value of _mbsncpy used directly in a conditional expression. |
| test.cpp:115:9:115:18 | call to _strncpy_l | Return Value of _strncpy_l used directly in a conditional expression. |
| test.cpp:119:9:119:18 | call to _wcsncpy_l | Return Value of _wcsncpy_l used directly in a conditional expression. |
| test.cpp:123:9:123:18 | call to _mbsncpy_l | Return Value of _mbsncpy_l used directly in a conditional expression. |
| test.cpp:127:6:127:34 | ! ... | Return Value of strncpy used in a logical operation. |
| test.cpp:127:7:127:13 | call to strncpy | Return Value of strncpy used as boolean. |
| test.cpp:131:11:131:17 | call to strncpy | Return Value of strncpy used as boolean. |
| test.cpp:133:16:133:44 | ! ... | Return Value of strncpy used in a logical operation. |
| test.cpp:133:17:133:23 | call to strncpy | Return Value of strncpy used as boolean. |
| test.cpp:135:11:135:16 | call to strcpy | Return Value of strcpy used as boolean. |
| test.cpp:135:11:135:37 | ... && ... | Return Value of strcpy used in a logical operation. |
| test.cpp:137:11:137:37 | ... == ... | Return Value of strcpy used in a logical operation. |
| test.cpp:139:11:139:37 | ... != ... | Return Value of strcpy used in a logical operation. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
typedef unsigned int size_t;
typedef int* locale_t;

char* strcpy(char* destination, const char* source)
{
return destination;
}
char* strncpy(char* destination, const char* source, size_t count)
{
return destination;
}

int SomeFunction()
{
return 1;
}

int SomeFunctionThatTakesString(char* destination)
{
return 1;
}

int strcmp(char* destination, const char* source)
{
return 1;
}

void PositiveCases()
{
char szbuf1[100];
char szbuf2[100];
int result;

if (strcpy(szbuf1, "test")) // Bug, direct usage
{
}

if (!strcpy(szbuf1, "test")) // Bug, unary binary operator
{
}

if (strcpy(szbuf1, "test") == 0) // Bug, equality operator
{
}

if (SomeFunction() && strcpy(szbuf1, "test")) // Bug, binary logical operator
{
}

if (strncpy(szbuf1, "test", 100)) // Bug
{
}

if (!strncpy(szbuf1, "test", 100)) // Bug
{
}

result = !strncpy(szbuf1, "test", 100);

result = strcpy(szbuf1, "test") && 1;

result = strcpy(szbuf1, "test") == 0;

result = strcpy(szbuf1, "test") != 0;
}

void NegativeCases()
{
char szbuf1[100];

if (SomeFunction())
{
}

if (SomeFunctionThatTakesString(strcpy(szbuf1, "test")))
{
}

if (strcmp(szbuf1, "test"))
{
}

}
Loading