Skip to content

Commit 57732ee

Browse files
authored
Merge pull request #1008 from geoffw0/wprintf
CPP: Clean up and fix FormattingFunction, FormatLiteral
2 parents 6939373 + 2bac7f1 commit 57732ee

File tree

7 files changed

+76
-41
lines changed

7 files changed

+76
-41
lines changed

cpp/ql/src/semmle/code/cpp/commons/Printf.qll

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,9 @@ class FormatLiteral extends Literal {
215215
/**
216216
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
217217
* a `char *` (either way, `%S` will have the opposite meaning).
218+
* DEPRECATED: Use getDefaultCharType() instead.
218219
*/
219-
predicate isWideCharDefault() {
220+
deprecated predicate isWideCharDefault() {
220221
getUse().getTarget().(FormattingFunction).isWideCharDefault()
221222
}
222223

@@ -671,37 +672,34 @@ class FormatLiteral extends Literal {
671672
}
672673

673674
/**
674-
* Gets the 'effective' char type character, that is, 'c' (meaning a `char`) or
675-
* 'C' (meaning a `wchar_t`).
676-
* - in the base case this is the same as the format type character.
677-
* - for a `wprintf` or similar function call, the meanings are reversed.
678-
* - the size prefixes 'l'/'w' (long) and 'h' (short) override the
679-
* type character to effectively 'C' or 'c' respectively.
675+
* Gets the char type required by the nth conversion specifier.
676+
* - in the base case this is the default for the formatting function
677+
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
678+
* - the `%S` format character reverses wideness.
679+
* - the size prefixes 'l'/'w' and 'h' override the type character
680+
* to wide or single-byte characters respectively.
680681
*/
681-
private string getEffectiveCharConversionChar(int n) {
682-
exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "c" or conv = "C") |
683-
(len = "l" and result = "C") or
684-
(len = "w" and result = "C") or
685-
(len = "h" and result = "c") or
686-
(len != "l" and len != "w" and len != "h" and (result = "c" or result = "C") and (if isWideCharDefault() then result != conv else result = conv))
687-
)
688-
}
689-
690682
private Type getConversionType1b(int n) {
691-
exists(string cnv | cnv = this.getEffectiveCharConversionChar(n) |
683+
exists(string len, string conv |
684+
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
692685
(
693-
cnv = "c" and
694-
result instanceof CharType and
695-
not result.(CharType).isExplicitlySigned() and
696-
not result.(CharType).isExplicitlyUnsigned()
697-
) or (
698-
cnv = "C" and
699-
isMicrosoft() and
700-
result instanceof WideCharType
701-
) or (
702-
cnv = "C" and
703-
not isMicrosoft() and
704-
result.hasName("wint_t")
686+
(
687+
(conv = "c" or conv = "C") and
688+
len = "h" and
689+
result instanceof PlainCharType
690+
) or (
691+
(conv = "c" or conv = "C") and
692+
(len = "l" or len = "w") and
693+
result = getWideCharType()
694+
) or (
695+
conv = "c" and
696+
(len != "l" and len != "w" and len != "h") and
697+
result = getDefaultCharType()
698+
) or (
699+
conv = "C" and
700+
(len != "l" and len != "w" and len != "h") and
701+
result = getNonDefaultCharType()
702+
)
705703
)
706704
)
707705
}
@@ -846,15 +844,7 @@ class FormatLiteral extends Literal {
846844
len = 1
847845
or (
848846
this.getConversionChar(n).toLowerCase()="c" and
849-
if (this.getEffectiveCharConversionChar(n)="C" and
850-
not isMicrosoft() and
851-
not isWideCharDefault()) then (
852-
len = 6 // MB_LEN_MAX
853-
// the wint_t (wide character) argument is converted
854-
// to a multibyte sequence by a call to the wcrtomb(3)
855-
) else (
856-
len = 1 // e.g. 'a'
857-
)
847+
len = 1 // e.g. 'a'
858848
) or this.getConversionChar(n).toLowerCase()="f" and
859849
exists(int dot, int afterdot |
860850
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1)

cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,22 @@ abstract class FormattingFunction extends Function {
4949
/**
5050
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
5151
* a `char *` (either way, `%S` will have the opposite meaning).
52+
*
53+
* DEPRECATED: Use getDefaultCharType() instead.
5254
*/
53-
predicate isWideCharDefault() { none() }
55+
deprecated predicate isWideCharDefault() { none() }
5456

5557
/**
5658
* Gets the default character type expected for `%s` by this function. Typically
5759
* `char` or `wchar_t`.
5860
*/
5961
Type getDefaultCharType() {
60-
result = stripTopLevelSpecifiersOnly(getParameter(getFormatParameterIndex()).getType().
61-
getUnderlyingType().(PointerType).getBaseType())
62+
result =
63+
stripTopLevelSpecifiersOnly(
64+
stripTopLevelSpecifiersOnly(
65+
getParameter(getFormatParameterIndex()).getType().getUnderlyingType()
66+
).(PointerType).getBaseType()
67+
)
6268
}
6369

6470
/**

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ typedef struct _IO_FILE FILE;
1212
extern int printf(const char *fmt, ...);
1313
extern int vprintf(const char *fmt, va_list ap);
1414
extern int vfprintf(FILE *stream, const char *format, va_list ap);
15+
extern int wprintf(const wchar_t *format, ...);
1516

1617
#include "printf1.h"
1718
#include "real_world.h"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| common.h:12:12:12:17 | printf | char | wchar_t | wchar_t |
2+
| common.h:15:12:15:18 | wprintf | wchar_t | char | wchar_t |
23
| format.h:4:13:4:17 | error | char | wchar_t | wchar_t |
34
| real_world.h:8:12:8:18 | fprintf | char | wchar_t | wchar_t |
45
| real_world.h:33:6:33:12 | msg_out | char | wchar_t | wchar_t |

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_unsigned_chars/printf1.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,21 @@ void fun1(unsigned char* a, unsigned char* b) {
101101
printf("%td\n", pdt); // GOOD
102102
printf("%td\n", a-b); // GOOD
103103
}
104+
105+
typedef unsigned int wint_t;
106+
107+
void test_chars(char c, wchar_t wc, wint_t wt)
108+
{
109+
printf("%c", c); // GOOD
110+
printf("%c", wc); // BAD [NOT DETECTED]
111+
printf("%c", wt); // BAD [NOT DETECTED]
112+
printf("%C", c); // BAD [NOT DETECTED]
113+
printf("%C", wc); // GOOD (converts to wint_t)
114+
printf("%C", wt); // GOOD
115+
wprintf(L"%c", c); // GOOD
116+
wprintf(L"%c", wc); // BAD [NOT DETECTED]
117+
wprintf(L"%c", wt); // BAD [NOT DETECTED]
118+
wprintf(L"%C", c); // BAD [NOT DETECTED]
119+
wprintf(L"%C", wc); // GOOD (converts to wint_t)
120+
wprintf(L"%C", wt); // GOOD
121+
}

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ typedef struct _IO_FILE FILE;
1212
extern int printf(const char *fmt, ...);
1313
extern int vprintf(const char *fmt, va_list ap);
1414
extern int vfprintf(FILE *stream, const char *format, va_list ap);
15+
extern int wprintf(const wchar_t *format, ...);
1516

1617
#include "printf1.h"
1718
#include "real_world.h"

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Microsoft/printf1.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,21 @@ void fun1(unsigned char* a, unsigned char* b) {
101101
printf("%td\n", pdt); // GOOD
102102
printf("%td\n", a-b); // GOOD
103103
}
104+
105+
typedef unsigned int wint_t;
106+
107+
void test_chars(char c, wchar_t wc, wint_t wt)
108+
{
109+
printf("%c", c); // GOOD
110+
printf("%c", wc); // BAD [NOT DETECTED]
111+
printf("%c", wt); // BAD [NOT DETECTED]
112+
printf("%C", c); // BAD [NOT DETECTED]
113+
printf("%C", wc); // GOOD (converts to wint_t)
114+
printf("%C", wt); // GOOD
115+
wprintf(L"%c", c); // BAD [NOT DETECTED]
116+
wprintf(L"%c", wc); // GOOD (converts to wint_t)
117+
wprintf(L"%c", wt); // GOOD
118+
wprintf(L"%C", c); // GOOD
119+
wprintf(L"%C", wc); // BAD [NOT DETECTED]
120+
wprintf(L"%C", wt); // BAD [NOT DETECTED]
121+
}

0 commit comments

Comments
 (0)