Skip to content

Commit 9219214

Browse files
authored
Merge pull request #695 from raulgarciamsft/users/raulga/c6324
cpp - Using the return value of a strcpy or related string copy function in an if statement
2 parents 7973460 + 18bb669 commit 9219214

File tree

7 files changed

+405
-0
lines changed

7 files changed

+405
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if(strcpy(szbuf1, "Manager") == 0) // most likely strcmp was intended instead of strcpy
2+
{
3+
// ...
4+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>This rule finds uses of the string copy function calls that return the <code>destination</code> parameter,
8+
and that do not have a return value reserved to indicate an error.</p>
9+
10+
<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>
11+
12+
<p>The string copy functions that the rule takes into consideration are: </p>
13+
<ul>
14+
<li>strcpy</li>
15+
<li>wcscpy</li>
16+
<li>_mbscpy</li>
17+
<li>strncpy</li>
18+
<li>_strncpy_l</li>
19+
<li>wcsncpy</li>
20+
<li>_wcsncpy_l</li>
21+
<li>_mbsncpy</li>
22+
<li>_mbsncpy_l</li>
23+
</ul>
24+
25+
<p>NOTE: It is highly recommended to consider using a more secure version of string manipulation functions suchas as <code>strcpy_s</code>.</p>
26+
27+
</overview>
28+
<recommendation>
29+
<p>Check to ensure that the flagged expressions are not typos.</p>
30+
<p>If a string comparison is intended, change the function to the appropriate string comparison function.</p>
31+
<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>
32+
33+
</recommendation>
34+
<example><sample src="UsingStrcpyAsBoolean.cpp" />
35+
</example>
36+
37+
<references>
38+
<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>
39+
<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>
40+
<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>
41+
42+
</references>
43+
</qhelp>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @name Using the return value of a strcpy or related string copy function as a boolean operator
3+
* @description The return value for strcpy, strncpy, or related string copy functions have no reserved return value to indicate an error.
4+
* Using the return values of these functions as boolean function .
5+
* 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).
6+
* @kind problem
7+
* @problem.severity error
8+
* @precision high
9+
* @id cpp/string-copy-return-value-as-boolean
10+
* @tags external/microsoft/C6324
11+
*/
12+
13+
import cpp
14+
import semmle.code.cpp.dataflow.DataFlow
15+
16+
predicate isStringComparisonFunction(string functionName) {
17+
functionName = "strcpy"
18+
or functionName = "wcscpy"
19+
or functionName = "_mbscpy"
20+
or functionName = "strncpy"
21+
or functionName = "_strncpy_l"
22+
or functionName = "wcsncpy"
23+
or functionName = "_wcsncpy_l"
24+
or functionName = "_mbsncpy"
25+
or functionName = "_mbsncpy_l"
26+
}
27+
28+
predicate isBoolean( Expr e1 )
29+
{
30+
exists ( Type t1 |
31+
t1 = e1.getType() and
32+
(t1.hasName("bool") or t1.hasName("BOOL") or t1.hasName("_Bool"))
33+
)
34+
}
35+
36+
predicate isStringCopyCastedAsBoolean( FunctionCall func, Expr expr1, string msg ) {
37+
DataFlow::localFlow(DataFlow::exprNode(func), DataFlow::exprNode(expr1))
38+
and isBoolean( expr1.getConversion*())
39+
and isStringComparisonFunction( func.getTarget().getQualifiedName())
40+
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used as boolean."
41+
}
42+
43+
predicate isStringCopyUsedInLogicalOperationOrCondition( FunctionCall func, Expr expr1, string msg ) {
44+
isStringComparisonFunction( func.getTarget().getQualifiedName() )
45+
and (((
46+
// it is being used in an equality or logical operation
47+
exists( EqualityOperation eop |
48+
eop = expr1
49+
and func = eop.getAChild()
50+
)
51+
or exists( UnaryLogicalOperation ule |
52+
expr1 = ule
53+
and func = ule.getAChild()
54+
)
55+
or exists( BinaryLogicalOperation ble |
56+
expr1 = ble
57+
and func = ble.getAChild()
58+
)
59+
)
60+
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used in a logical operation."
61+
)
62+
or
63+
exists( ConditionalStmt condstmt |
64+
condstmt.getAChild() = expr1 |
65+
// or the string copy function is used directly as the conditional expression
66+
func = condstmt.getChild(0)
67+
and msg = "Return Value of " + func.getTarget().getQualifiedName() + " used directly in a conditional expression."
68+
))
69+
}
70+
71+
from FunctionCall func, Expr expr1, string msg
72+
where
73+
( isStringCopyCastedAsBoolean(func, expr1, msg) and
74+
not isStringCopyUsedInLogicalOperationOrCondition(func, expr1, _)
75+
)
76+
or isStringCopyUsedInLogicalOperationOrCondition(func, expr1, msg)
77+
select expr1, msg
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
| test.c:34:9:34:14 | call to strcpy | Return Value of strcpy used directly in a conditional expression. |
2+
| test.c:38:9:38:31 | ! ... | Return Value of strcpy used in a logical operation. |
3+
| test.c:42:9:42:35 | ... == ... | Return Value of strcpy used in a logical operation. |
4+
| test.c:46:9:46:48 | ... && ... | Return Value of strcpy used in a logical operation. |
5+
| test.c:50:9:50:15 | call to strncpy | Return Value of strncpy used directly in a conditional expression. |
6+
| test.c:54:6:54:34 | ! ... | Return Value of strncpy used in a logical operation. |
7+
| test.c:58:11:58:39 | ! ... | Return Value of strncpy used in a logical operation. |
8+
| test.c:60:11:60:37 | ... && ... | Return Value of strcpy used in a logical operation. |
9+
| test.c:62:11:62:37 | ... == ... | Return Value of strcpy used in a logical operation. |
10+
| test.c:64:11:64:37 | ... != ... | Return Value of strcpy used in a logical operation. |
11+
| test.cpp:75:9:75:14 | call to strcpy | Return Value of strcpy used directly in a conditional expression. |
12+
| test.cpp:79:9:79:31 | ! ... | Return Value of strcpy used in a logical operation. |
13+
| test.cpp:79:10:79:15 | call to strcpy | Return Value of strcpy used as boolean. |
14+
| test.cpp:83:9:83:35 | ... == ... | Return Value of strcpy used in a logical operation. |
15+
| test.cpp:87:9:87:48 | ... && ... | Return Value of strcpy used in a logical operation. |
16+
| test.cpp:87:27:87:32 | call to strcpy | Return Value of strcpy used as boolean. |
17+
| test.cpp:91:9:91:37 | call to wcscpy | Return Value of wcscpy used directly in a conditional expression. |
18+
| test.cpp:95:9:95:14 | call to wcscpy | Return Value of wcscpy used directly in a conditional expression. |
19+
| test.cpp:99:9:99:15 | call to _mbscpy | Return Value of _mbscpy used directly in a conditional expression. |
20+
| test.cpp:103:9:103:15 | call to strncpy | Return Value of strncpy used directly in a conditional expression. |
21+
| test.cpp:107:9:107:15 | call to wcsncpy | Return Value of wcsncpy used directly in a conditional expression. |
22+
| test.cpp:111:9:111:16 | call to _mbsncpy | Return Value of _mbsncpy used directly in a conditional expression. |
23+
| test.cpp:115:9:115:18 | call to _strncpy_l | Return Value of _strncpy_l used directly in a conditional expression. |
24+
| test.cpp:119:9:119:18 | call to _wcsncpy_l | Return Value of _wcsncpy_l used directly in a conditional expression. |
25+
| test.cpp:123:9:123:18 | call to _mbsncpy_l | Return Value of _mbsncpy_l used directly in a conditional expression. |
26+
| test.cpp:127:6:127:34 | ! ... | Return Value of strncpy used in a logical operation. |
27+
| test.cpp:127:7:127:13 | call to strncpy | Return Value of strncpy used as boolean. |
28+
| test.cpp:131:11:131:17 | call to strncpy | Return Value of strncpy used as boolean. |
29+
| test.cpp:133:16:133:44 | ! ... | Return Value of strncpy used in a logical operation. |
30+
| test.cpp:133:17:133:23 | call to strncpy | Return Value of strncpy used as boolean. |
31+
| test.cpp:135:11:135:16 | call to strcpy | Return Value of strcpy used as boolean. |
32+
| test.cpp:135:11:135:37 | ... && ... | Return Value of strcpy used in a logical operation. |
33+
| test.cpp:137:11:137:37 | ... == ... | Return Value of strcpy used in a logical operation. |
34+
| test.cpp:139:11:139:37 | ... != ... | Return Value of strcpy used in a logical operation. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
typedef unsigned int size_t;
2+
typedef int* locale_t;
3+
4+
char* strcpy(char* destination, const char* source)
5+
{
6+
return destination;
7+
}
8+
char* strncpy(char* destination, const char* source, size_t count)
9+
{
10+
return destination;
11+
}
12+
13+
int SomeFunction()
14+
{
15+
return 1;
16+
}
17+
18+
int SomeFunctionThatTakesString(char* destination)
19+
{
20+
return 1;
21+
}
22+
23+
int strcmp(char* destination, const char* source)
24+
{
25+
return 1;
26+
}
27+
28+
void PositiveCases()
29+
{
30+
char szbuf1[100];
31+
char szbuf2[100];
32+
int result;
33+
34+
if (strcpy(szbuf1, "test")) // Bug, direct usage
35+
{
36+
}
37+
38+
if (!strcpy(szbuf1, "test")) // Bug, unary binary operator
39+
{
40+
}
41+
42+
if (strcpy(szbuf1, "test") == 0) // Bug, equality operator
43+
{
44+
}
45+
46+
if (SomeFunction() && strcpy(szbuf1, "test")) // Bug, binary logical operator
47+
{
48+
}
49+
50+
if (strncpy(szbuf1, "test", 100)) // Bug
51+
{
52+
}
53+
54+
if (!strncpy(szbuf1, "test", 100)) // Bug
55+
{
56+
}
57+
58+
result = !strncpy(szbuf1, "test", 100);
59+
60+
result = strcpy(szbuf1, "test") && 1;
61+
62+
result = strcpy(szbuf1, "test") == 0;
63+
64+
result = strcpy(szbuf1, "test") != 0;
65+
}
66+
67+
void NegativeCases()
68+
{
69+
char szbuf1[100];
70+
71+
if (SomeFunction())
72+
{
73+
}
74+
75+
if (SomeFunctionThatTakesString(strcpy(szbuf1, "test")))
76+
{
77+
}
78+
79+
if (strcmp(szbuf1, "test"))
80+
{
81+
}
82+
83+
}

0 commit comments

Comments
 (0)