Skip to content

Commit a7c69ba

Browse files
author
ihsinme
committed
create new branchihsinme-patch-87 in fork
1 parent 28dca3f commit a7c69ba

File tree

6 files changed

+338
-0
lines changed

6 files changed

+338
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
...
3+
mbtowc(&wc, ptr, 4)); // BAD:we can get unpredictable results
4+
...
5+
mbtowc(&wc, ptr, MB_LEN_MAX); // GOOD
6+
...
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p> Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. If you are sure you are working with a null-terminated string, use the length macros, if not, use the correctly computed length.</p>
7+
8+
</overview>
9+
10+
<example>
11+
<p>The following example shows the erroneous and corrected method of using function mbtowc.</p>
12+
<sample src="DangerousUseMbtowc.cpp" />
13+
14+
</example>
15+
<references>
16+
17+
<li>
18+
CERT Coding Standard:
19+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts - SEI CERT C Coding Standard - Confluence</a>.
20+
</li>
21+
22+
</references>
23+
</qhelp>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* @name Dangerous use mbtowc.
3+
* @description Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result.
4+
* @kind problem
5+
* @id cpp/dangerous-use-mbtowc
6+
* @problem.severity warning
7+
* @precision medium
8+
* @tags correctness
9+
* security
10+
* external/cwe/cwe-125
11+
*/
12+
13+
import cpp
14+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
15+
16+
/** Holds if there are indications that the variable is treated as a string. */
17+
predicate exprMayBeString(Expr exp) {
18+
(
19+
exists(StringLiteral sl | globalValueNumber(exp) = globalValueNumber(sl))
20+
or
21+
exists(FunctionCall fctmp |
22+
globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) and
23+
fctmp.getTarget().hasGlobalOrStdName(["strlen", "strcat", "strncat", "strcpy", "sptintf"])
24+
)
25+
or
26+
exists(AssignExpr astmp |
27+
astmp.getRValue().getValue() = "0" and
28+
astmp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
29+
exp.(VariableAccess).getTarget()
30+
)
31+
or
32+
exists(ComparisonOperation cotmp, Expr exptmp1, Expr exptmp2 |
33+
exptmp1.getValue() = "0" and
34+
(
35+
exptmp2.(PointerDereferenceExpr).getOperand().(VariableAccess).getTarget() =
36+
exp.(VariableAccess).getTarget() or
37+
exptmp2.(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
38+
exp.getAChild().(VariableAccess).getTarget()
39+
) and
40+
cotmp.hasOperands(exptmp1, exptmp2)
41+
)
42+
)
43+
}
44+
45+
/** Holds if expression is constant or operator call `sizeof`. */
46+
predicate argConstOrSizeof(Expr exp) {
47+
exp.getValue().toInt() > 1 or
48+
exp.(SizeofTypeOperator).getTypeOperand().getSize() > 1
49+
}
50+
51+
/** Holds if expression is macro. */
52+
predicate argMacro(Expr exp) {
53+
exists(MacroInvocation matmp |
54+
exp = matmp.getExpr() and
55+
(
56+
matmp.getMacroName() = "MB_LEN_MAX" or
57+
matmp.getMacroName() = "MB_CUR_MAX"
58+
)
59+
)
60+
}
61+
62+
from FunctionCall fc, string msg
63+
where
64+
exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and
65+
fc.getTarget().hasGlobalOrStdName(["mbtowc", "mbrtowc"]) and
66+
not fc.getArgument(0).isConstant() and
67+
not fc.getArgument(1).isConstant() and
68+
(
69+
exprMayBeString(fc.getArgument(1)) and
70+
argConstOrSizeof(fc.getArgument(2)) and
71+
fc.getArgument(2).getValue().toInt() < 5 and
72+
not argMacro(fc.getArgument(2)) and
73+
msg = "Size can be less than maximum character length, use macro MB_CUR_MAX."
74+
or
75+
not exprMayBeString(fc.getArgument(1)) and
76+
(
77+
argConstOrSizeof(fc.getArgument(2))
78+
or
79+
argMacro(fc.getArgument(2))
80+
or
81+
exists(DecrementOperation dotmp |
82+
globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and
83+
not exists(AssignSubExpr aetmp |
84+
(
85+
aetmp.getLValue().(VariableAccess).getTarget() =
86+
fc.getArgument(2).(VariableAccess).getTarget() or
87+
globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2))
88+
) and
89+
globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc)
90+
)
91+
)
92+
) and
93+
msg =
94+
"Access beyond the allocated memory is possible, the length can change without changing the pointer."
95+
or
96+
exists(AssignPointerAddExpr aetmp |
97+
(
98+
aetmp.getLValue().(VariableAccess).getTarget() =
99+
fc.getArgument(0).(VariableAccess).getTarget() or
100+
globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0))
101+
) and
102+
globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc)
103+
) and
104+
msg = "Maybe you're using the function's return value incorrectly."
105+
)
106+
select fc, msg
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| test.cpp:61:27:61:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. |
2+
| test.cpp:70:27:70:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. |
3+
| test.cpp:98:11:98:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
4+
| test.cpp:114:11:114:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
5+
| test.cpp:130:11:130:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
6+
| test.cpp:147:11:147:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
7+
| test.cpp:169:11:169:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
8+
| test.cpp:185:11:185:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
typedef unsigned long size_t;
2+
#define MB_CUR_MAX 6
3+
#define MB_LEN_MAX 16
4+
int mbtowc(wchar_t *out, const char *in, size_t size);
5+
int wprintf (const wchar_t* format, ...);
6+
int strlen( const char * string );
7+
int checkErrors();
8+
9+
void goodTest0()
10+
{
11+
char * ptr = "123456789";
12+
int ret;
13+
int len;
14+
len = 9;
15+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD
16+
wprintf(L"%lc", wc);
17+
}
18+
}
19+
void goodTest1(const char* ptr)
20+
{
21+
int ret;
22+
int len;
23+
len = strlen(ptr);
24+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD
25+
wprintf(L"%lc", wc);
26+
}
27+
}
28+
void goodTest2(char* ptr)
29+
{
30+
int ret;
31+
ptr[10]=0;
32+
int len = 9;
33+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, 16)) > 0; len-=ret) { // GOOD
34+
wprintf(L"%lc", wc);
35+
}
36+
}
37+
38+
void goodTest3(const char* ptr)
39+
{
40+
int ret;
41+
int len;
42+
len = strlen(ptr);
43+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_CUR_MAX)) > 0; len-=ret) { // GOOD
44+
wprintf(L"%lc", wc);
45+
}
46+
}
47+
void goodTest4(const char* ptr)
48+
{
49+
int ret;
50+
int len;
51+
len = strlen(ptr);
52+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_LEN_MAX)) > 0; len-=ret) { // GOOD
53+
wprintf(L"%lc", wc);
54+
}
55+
}
56+
void badTest1(const char* ptr)
57+
{
58+
int ret;
59+
int len;
60+
len = strlen(ptr);
61+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, 4)) > 0; len-=ret) { // BAD:we can get unpredictable results
62+
wprintf(L"%lc", wc);
63+
}
64+
}
65+
void badTest2(const char* ptr)
66+
{
67+
int ret;
68+
int len;
69+
len = strlen(ptr);
70+
for (wchar_t wc; (ret = mbtowc(&wc, ptr, sizeof(wchar_t))) > 0; len-=ret) { // BAD:we can get unpredictable results
71+
wprintf(L"%lc", wc);
72+
}
73+
}
74+
75+
void goodTest5(const char* ptr,wchar_t *wc,int wc_len)
76+
{
77+
int ret;
78+
int len;
79+
len = wc_len;
80+
while (*ptr && len > 0) {
81+
ret = mbtowc(wc, ptr, len); // GOOD
82+
if (ret <0)
83+
break;
84+
if (ret == 0 || ret > len)
85+
break;
86+
len-=ret;
87+
ptr+=ret;
88+
wc++;
89+
}
90+
}
91+
92+
void badTest3(const char* ptr,wchar_t *wc,int wc_len)
93+
{
94+
int ret;
95+
int len;
96+
len = wc_len;
97+
while (*ptr && len > 0) {
98+
ret = mbtowc(wc, ptr, MB_CUR_MAX); // BAD
99+
if (ret <0)
100+
break;
101+
if (ret == 0 || ret > len)
102+
break;
103+
len-=ret;
104+
ptr+=ret;
105+
wc++;
106+
}
107+
}
108+
void badTest4(const char* ptr,wchar_t *wc,int wc_len)
109+
{
110+
int ret;
111+
int len;
112+
len = wc_len;
113+
while (*ptr && len > 0) {
114+
ret = mbtowc(wc, ptr, 16); // BAD
115+
if (ret <0)
116+
break;
117+
if (ret == 0 || ret > len)
118+
break;
119+
len-=ret;
120+
ptr+=ret;
121+
wc++;
122+
}
123+
}
124+
void badTest5(const char* ptr,wchar_t *wc,int wc_len)
125+
{
126+
int ret;
127+
int len;
128+
len = wc_len;
129+
while (*ptr && len > 0) {
130+
ret = mbtowc(wc, ptr, sizeof(wchar_t)); // BAD
131+
if (ret <0)
132+
break;
133+
if (ret == 0 || ret > len)
134+
break;
135+
len-=ret;
136+
ptr+=ret;
137+
wc++;
138+
}
139+
}
140+
141+
void badTest6(const char* ptr,wchar_t *wc,int wc_len)
142+
{
143+
int ret;
144+
int len;
145+
len = wc_len;
146+
while (*ptr && wc_len > 0) {
147+
ret = mbtowc(wc, ptr, wc_len); // BAD
148+
if (ret <0)
149+
if (checkErrors()) {
150+
++ptr;
151+
--len;
152+
continue;
153+
} else
154+
break;
155+
if (ret == 0 || ret > len)
156+
break;
157+
wc_len--;
158+
len-=ret;
159+
wc++;
160+
ptr+=ret;
161+
}
162+
}
163+
void badTest7(const char* ptr,wchar_t *wc,int wc_len)
164+
{
165+
int ret;
166+
int len;
167+
len = wc_len;
168+
while (*ptr && wc_len > 0) {
169+
ret = mbtowc(wc, ptr, len); // BAD
170+
if (ret <0)
171+
break;
172+
if (ret == 0 || ret > len)
173+
break;
174+
len--;
175+
wc++;
176+
ptr+=ret;
177+
}
178+
}
179+
void badTest8(const char* ptr,wchar_t *wc)
180+
{
181+
int ret;
182+
int len;
183+
len = strlen(ptr);
184+
while (*ptr && len > 0) {
185+
ret = mbtowc(wc, ptr, len); // BAD
186+
if (ret <0)
187+
break;
188+
if (ret == 0 || ret > len)
189+
break;
190+
len-=ret;
191+
ptr++;
192+
wc+=ret;
193+
}
194+
}

0 commit comments

Comments
 (0)