CPP: Handle %s/%c/%S/%C correctly on non-MS platforms.

This commit is contained in:
Geoffrey White
2019-03-13 12:05:47 +00:00
parent 648cdbab6c
commit 975a0bbf0d
13 changed files with 50 additions and 44 deletions

View File

@@ -675,7 +675,7 @@ class FormatLiteral extends Literal {
* Gets the char type required by the nth conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
* - the `%S` format character reverses wideness.
* - the `%C` format character reverses wideness on some platforms.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/
@@ -722,7 +722,7 @@ class FormatLiteral extends Literal {
* Gets the string type required by the nth conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
* - the `%S` format character reverses wideness.
* - the `%S` format character reverses wideness on some platforms.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/

View File

@@ -22,7 +22,7 @@ private Type stripTopLevelSpecifiersOnly(Type t) {
*/
Type getAFormatterWideType() {
exists(FormattingFunction ff |
result = stripTopLevelSpecifiersOnly(ff.getDefaultCharType()) and
result = stripTopLevelSpecifiersOnly(ff.getFormatCharType()) and
result.getSize() != 1
)
}
@@ -46,6 +46,14 @@ abstract class FormattingFunction extends Function {
/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();
/**
* Holds if this `FormattingFunction` is in a context that supports
* Microsoft rules and extensions.
*/
predicate isMicrosoft() {
getFile().compiledAsMicrosoft()
}
/**
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
* a `char *` (either way, `%S` will have the opposite meaning).
@@ -71,12 +79,13 @@ abstract class FormattingFunction extends Function {
* `char` or `wchar_t`.
*/
Type getDefaultCharType() {
result =
stripTopLevelSpecifiersOnly(
stripTopLevelSpecifiersOnly(
getParameter(getFormatParameterIndex()).getType().getUnderlyingType()
).(PointerType).getBaseType()
)
(
isMicrosoft() and
result = getFormatCharType()
) or (
not isMicrosoft() and
result instanceof PlainCharType
)
}
/**

View File

@@ -1,10 +1,6 @@
| 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 *' |
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:27:17:27:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
| tests.cpp:34:36:34:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |

View File

@@ -1,3 +1,3 @@
| tests.cpp:8:5:8:10 | printf | char | char | char16_t, wchar_t | char16_t, wchar_t |
| tests.cpp:9:5:9:11 | wprintf | wchar_t | wchar_t | char | wchar_t |
| tests.cpp:10:5:10:12 | swprintf | char16_t | char16_t | char | char16_t |
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | char16_t, wchar_t | char16_t, wchar_t |
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t, wchar_t | char16_t, wchar_t |

View File

@@ -22,19 +22,19 @@ void tests() {
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
wprintf(L"%S", "Hello"); // GOOD
wprintf(L"%S", u"Hello"); // BAD: expecting char
wprintf(L"%S", L"Hello"); // BAD: expecting char
wprintf(L"%S", "Hello"); // BAD: expecting wchar_t [NOT DETECTED]
wprintf(L"%S", u"Hello"); // BAD: expecting wchar_t [NOT DETECTED]
wprintf(L"%S", L"Hello"); // GOOD
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
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
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // BAD: expecting char16_t [NOT DETECTED]
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // GOOD
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char16_t [NOT DETECTED]
}

View File

@@ -1,2 +1,3 @@
| printf.cpp:33:31:33:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
| printf.cpp:45:29:45:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
| printf.cpp:52:29:52:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |

View File

@@ -1,2 +1,2 @@
| printf.cpp:15:5:15:12 | swprintf | char16_t | char | char16_t |
| printf.cpp:15:5:15:12 | swprintf | char | char16_t | char16_t |
| printf.cpp:26:5:26:11 | sprintf | char | char16_t | char16_t |

View File

@@ -30,7 +30,7 @@ int sprintf(char *dest, char *format, ...);
void test1() {
WCHAR string[20];
swprintf(string, u"test %s", u"test"); // GOOD
swprintf(string, u"test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string
}
void test2() {

View File

@@ -11,8 +11,8 @@
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:125:18:125:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
| printf1.h:128:18:128:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
| printf1.h:126:18:126:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
| printf1.h:127:18:127:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -1,5 +1,5 @@
| common.h:12:12:12:17 | printf | char | wchar_t | wchar_t |
| common.h:15:12:15:18 | wprintf | wchar_t | char | wchar_t |
| common.h:15:12:15:18 | wprintf | char | wchar_t | wchar_t |
| format.h:4:13:4:17 | error | char | wchar_t | wchar_t |
| real_world.h:8:12:8:18 | fprintf | char | wchar_t | wchar_t |
| real_world.h:33:6:33:12 | msg_out | char | wchar_t | wchar_t |

View File

@@ -122,8 +122,8 @@ void test_chars(char c, wchar_t wc, wint_t wt)
void test_ws(char *c, wchar_t *wc)
{
wprintf(L"%s", c); // GOOD [FALSE POSITIVE]
wprintf(L"%s", wc); // BAD [NOT DETECTED]
wprintf(L"%S", c); // BAD [NOT DETECTED]
wprintf(L"%S", wc); // GOOD [FALSE POSITIVE]
wprintf(L"%s", c); // GOOD
wprintf(L"%s", wc); // BAD
wprintf(L"%S", c); // BAD
wprintf(L"%S", wc); // GOOD
}

View File

@@ -17,8 +17,8 @@
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
| printf1.h:84:23:84:35 | ... - ... | This argument should be of type 'ssize_t' but is of type 'long long' |
| printf1.h:125:18:125:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
| printf1.h:128:18:128:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
| printf1.h:126:18:126:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
| printf1.h:127:18:127:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -123,7 +123,7 @@ void test_chars(char c, wchar_t wc, wint_t wt)
void test_ws(char *c, wchar_t *wc, wint_t *wt)
{
wprintf(L"%s", c); // BAD [NOT DETECTED]
wprintf(L"%s", wc); // GOOD
wprintf(L"%S", c); // GOOD
wprintf(L"%s", wc); // GOOD [FALSE POSITIVE]
wprintf(L"%S", c); // GOOD [FALSE POSITIVE]
wprintf(L"%S", wc); // BAD [NOT DETECTED]
}