-
Notifications
You must be signed in to change notification settings - Fork 1.8k
CPP: Fix WrongTypeFormatArguments.ql char16_t * issues (and others) #286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a3459dd
5b8925c
fe8f7e9
6e5207c
e74721e
39f030b
2af56b8
8005558
1af6c10
e2be19b
89c5648
580471a
2841897
94ff2e5
605db44
67a7b75
998b28b
99816d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,38 @@ | |
*/ | ||
|
||
import semmle.code.cpp.Function | ||
|
||
private Type stripTopLevelSpecifiersOnly(Type t) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was it necessary to introduce this? The commit that introduced it didn't come with any test output changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does have test changes, but in a test that's still internal (see internal PR https://git.semmle.com/Semmle/code/pull/28289). Having said this, looking at it again I think I can do better. I'll do this as follow-up work. |
||
( | ||
result = stripTopLevelSpecifiersOnly(t.(SpecifiedType).getBaseType()) | ||
) or ( | ||
result = t and | ||
not t instanceof SpecifiedType | ||
) | ||
} | ||
|
||
/** | ||
* A type that is used as a format string by any formatting function. | ||
*/ | ||
Type getAFormatterWideType() { | ||
exists(FormattingFunction ff | | ||
result = stripTopLevelSpecifiersOnly(ff.getDefaultCharType()) and | ||
result.getSize() != 1 | ||
) | ||
} | ||
|
||
/** | ||
* A type that is used as a format string by any formatting function, or `wchar_t` if | ||
* there is none. | ||
*/ | ||
private Type getAFormatterWideTypeOrDefault() { | ||
result = getAFormatterWideType() or | ||
( | ||
not exists(getAFormatterWideType()) and | ||
result instanceof Wchar_t | ||
) | ||
} | ||
|
||
/** | ||
* A standard library function that uses a `printf`-like formatting string. | ||
*/ | ||
|
@@ -20,6 +52,43 @@ abstract class FormattingFunction extends Function { | |
*/ | ||
predicate isWideCharDefault() { none() } | ||
|
||
/** | ||
* Gets the default character type expected for `%s` by this function. Typically | ||
* `char` or `wchar_t`. | ||
*/ | ||
Type getDefaultCharType() { | ||
result = stripTopLevelSpecifiersOnly(getParameter(getFormatParameterIndex()).getType(). | ||
getUnderlyingType().(PointerType).getBaseType()) | ||
} | ||
|
||
/** | ||
* Gets the non-default character type expected for `%S` by this function. Typically | ||
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell | ||
* which is correct for a particular function. | ||
*/ | ||
Type getNonDefaultCharType() { | ||
( | ||
getDefaultCharType().getSize() = 1 and | ||
result = getAFormatterWideTypeOrDefault() | ||
) or ( | ||
getDefaultCharType().getSize() > 1 and | ||
result instanceof PlainCharType | ||
) | ||
} | ||
|
||
/** | ||
* Gets the wide character type for this function. This is usually `wchar_t`. On some | ||
* snapshots there may be multiple results where we can't tell which is correct for a | ||
* particular function. | ||
*/ | ||
Type getWideCharType() { | ||
( | ||
result = getDefaultCharType() or | ||
result = getNonDefaultCharType() | ||
) and | ||
result.getSize() > 1 | ||
} | ||
|
||
/** | ||
* Gets the position at which the output parameter, if any, occurs. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
| tests.cpp:18:15:18:22 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' | | ||
| tests.cpp:19:15:19:22 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' | | ||
| tests.cpp:25:17:25:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' | | ||
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' | | ||
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' | | ||
| tests.cpp:31:17:31:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' | | ||
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' | | ||
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' | | ||
| tests.cpp:38:36:38:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' | | ||
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Likely Bugs/Format/WrongTypeFormatArguments.ql |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
| tests.cpp:8:5:8:10 | printf | char | char16_t, wchar_t | char16_t, wchar_t | | ||
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t | | ||
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import cpp | ||
|
||
from FormattingFunction f | ||
select | ||
f, | ||
concat(f.getDefaultCharType().toString(), ", "), | ||
concat(f.getNonDefaultCharType().toString(), ", "), | ||
concat(f.getWideCharType().toString(), ", ") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Test for custom definitions of *wprintf using different types than the | ||
* platform wide character type. | ||
*/ | ||
|
||
typedef unsigned int size_t; | ||
|
||
int printf(const char * format, ...); | ||
int wprintf(const wchar_t * format, ...); // on wchar_t | ||
int swprintf(char16_t * s, size_t n, const char16_t * format, ...); // on char16_t | ||
|
||
#define BUF_SIZE (4096) | ||
|
||
void tests() { | ||
char16_t buffer[BUF_SIZE]; | ||
|
||
printf("%s", "Hello"); // GOOD | ||
printf("%s", u"Hello"); // BAD: expecting char | ||
printf("%s", L"Hello"); // BAD: expecting char | ||
|
||
printf("%S", "Hello"); // BAD: expecting wchar_t or char16_t [NOT DETECTED] | ||
printf("%S", u"Hello"); // GOOD | ||
printf("%S", L"Hello"); // GOOD | ||
|
||
wprintf(L"%s", "Hello"); // BAD: expecting wchar_t | ||
wprintf(L"%s", u"Hello"); // BAD: expecting wchar_t | ||
wprintf(L"%s", L"Hello"); // GOOD | ||
|
||
wprintf(L"%S", "Hello"); // GOOD | ||
wprintf(L"%S", u"Hello"); // BAD: expecting char | ||
wprintf(L"%S", L"Hello"); // BAD: expecting char | ||
|
||
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // BAD: expecting char16_t | ||
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD | ||
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t | ||
|
||
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // GOOD | ||
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // BAD: expecting char | ||
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
| tests_32.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' | | ||
| tests_64.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Likely Bugs/Format/WrongTypeFormatArguments.ql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this is only needed because the
where
clause in this query has its logic the wrong way around for when there are multipleexpected
types. I'd like to try fixing this, but I won't let it block this PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will look at this in a follow-up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, I'm thinking that
should be replaced by
If it's a UI issue to have multiple alert messages for the same line, we could use
strictconcat
to concatenate the names of the expected types instead of having aType expected
in thefrom
clause.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You were right - see #1255 and https://git.semmle.com/Semmle/code/pull/31630.