Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import cpp
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.ir.IR

class WideCharPointerType extends PointerType {
WideCharPointerType() { this.getBaseType() instanceof WideCharType }
Expand Down Expand Up @@ -108,7 +109,9 @@ where
// Avoid cases where the cast is guarded by a check to determine if
// unicode encoding is enabled in such a way to disallow the dangerous cast
// at runtime.
not isLikelyDynamicallyChecked(e1)
not isLikelyDynamicallyChecked(e1) and
// Avoid cases in unreachable blocks.
any(EnterFunctionInstruction e).getASuccessor+().getAst() = e1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine this transitive closure is sufficiently constrained so that this doesn't matter much, but in case DCA comes back with a performance problem it may be worth doing getASuccessor+() on basic blocks instead of individual instructions since that's much much more efficient (simply because there are a lot fewer basic blocks than there are instructions).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see any performance problems, but out of curiosity, how would I rewrite the code in that case?

  any(EnterFunctionInstruction e).getBlock().getASuccessor+() =
    any(Instruction i | i.getAst() = e1).getBlock()

Or is there any easier way?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like that, yeah. Probably with * instead of + to handle the case where e1 is in the same block as the entry point of the function.

select e1,
"Conversion from " + e1.getType().toString() + " to " + e2.getType().toString() +
". Use of invalid string can lead to undefined behavior."
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ void Test()
wchar_t *lpWchar = NULL;
LPCSTR lpcstr = "b";

lpWchar = (LPWSTR)"a"; // BUG
lpWchar = (LPWSTR)lpcstr; // BUG
lpWchar = (LPWSTR)"a"; // $ Alert
lpWchar = (LPWSTR)lpcstr; // $ Alert

lpWchar = (wchar_t*)lpChar; // BUG
lpWchar = (wchar_t*)lpChar; // $ Alert

fconstWChar((LPCWSTR)lpChar); // BUG
fWChar((LPWSTR)lpChar); // BUG
fconstWChar((LPCWSTR)lpChar); // $ Alert
fWChar((LPWSTR)lpChar); // $ Alert

lpChar = (LPSTR)"a"; // Valid
lpWchar = (LPWSTR)L"a"; // Valid
Expand Down Expand Up @@ -79,33 +79,64 @@ void CheckedConversionFalsePositiveTest3(unsigned short flags, LPTSTR buffer)
if(flags & UNICODE)
lpWchar = (LPWSTR)buffer; // GOOD
else
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert

if((flags & UNICODE) == 0x8)
lpWchar = (LPWSTR)buffer; // GOOD
else
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert

if((flags & UNICODE) != 0x8)
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert
else
lpWchar = (LPWSTR)buffer; // GOOD

// Bad operator precedence
if(flags & UNICODE == 0x8)
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert
else
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert

if((flags & UNICODE) != 0)
lpWchar = (LPWSTR)buffer; // GOOD
else
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert

if((flags & UNICODE) == 0)
lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert
else
lpWchar = (LPWSTR)buffer; // GOOD

lpWchar = (LPWSTR)buffer; // BUG
lpWchar = (LPWSTR)buffer; // $ Alert
}

typedef unsigned long long size_t;

size_t wcslen(const wchar_t *str);
size_t strlen(const char* str);

template<typename C>
size_t str_len(const C *str) {
if (sizeof(C) != 1) {
return wcslen((const wchar_t *)str); // GOOD -- unreachable code
}

return strlen((const char *)str);
}

template<typename C>
size_t wrong_str_len(const C *str) {
if (sizeof(C) == 1) {
return wcslen((const wchar_t *)str); // $ Alert
}

return strlen((const char *)str);
}

void test_str_len(const wchar_t *wstr, const char *str) {
size_t len =
str_len(wstr) +
str_len(str) +
wrong_str_len(wstr) +
wrong_str_len(str);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
| WcharCharConversion.cpp:103:21:103:26 | buffer | Conversion from LPTSTR to LPWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:106:21:106:26 | buffer | Conversion from LPTSTR to LPWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:110:20:110:25 | buffer | Conversion from LPTSTR to LPWSTR. Use of invalid string can lead to undefined behavior. |
| WcharCharConversion.cpp:130:34:130:36 | str | Conversion from const char * to const wchar_t *. Use of invalid string can lead to undefined behavior. |
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Security/CWE/CWE-704/WcharCharConversion.ql
query: Security/CWE/CWE-704/WcharCharConversion.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql