Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e2be19b

Browse files
committed
CPP: New mechanism for string types in printf.qll.
1 parent 1af6c10 commit e2be19b

4 files changed

Lines changed: 104 additions & 34 deletions

File tree

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

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,32 @@ class FormatLiteral extends Literal {
239239
getUse().getTarget().(FormattingFunction).isWideCharDefault()
240240
}
241241

242+
/**
243+
* Gets the default character type expected for `%s` by this format literal. Typically
244+
* `char` or `wchar_t`.
245+
*/
246+
Type getDefaultCharType() {
247+
result = getUse().getTarget().(FormattingFunction).getDefaultCharType()
248+
}
249+
250+
/**
251+
* Gets the non-default character type expected for `%S` by this format literal. Typically
252+
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
253+
* which is correct for a particular function.
254+
*/
255+
Type getNonDefaultCharType() {
256+
result = getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
257+
}
258+
259+
/**
260+
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
261+
* snapshots there may be multiple results where we can't tell which is correct for a
262+
* particular function.
263+
*/
264+
Type getWideCharType() {
265+
result = getUse().getTarget().(FormattingFunction).getWideCharType()
266+
}
267+
242268
/**
243269
* Holds if this `FormatLiteral` is in a context that supports
244270
* Microsoft rules and extensions.
@@ -648,7 +674,6 @@ class FormatLiteral extends Literal {
648674
result = getConversionType2(n) or
649675
result = getConversionType3(n) or
650676
result = getConversionType4(n) or
651-
result = getConversionType5(n) or
652677
result = getConversionType6(n) or
653678
result = getConversionType7(n) or
654679
result = getConversionType8(n) or
@@ -715,38 +740,34 @@ class FormatLiteral extends Literal {
715740
}
716741

717742
/**
718-
* Gets the 'effective' string type character, that is, 's' (meaning a char string) or
719-
* 'S' (meaning a wide string).
720-
* - in the base case this is the same as the format type character.
721-
* - for a `wprintf` or similar function call, the meanings are reversed.
722-
* - the size prefixes 'l'/'w' (long) and 'h' (short) override the
723-
* type character to effectively 'S' or 's' respectively.
743+
* Gets the string type required by the nth conversion specifier.
744+
* - in the base case this is the default for the formatting function
745+
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
746+
* - the `%S` format character reverses wideness.
747+
* - the size prefixes 'l'/'w' and 'h' override the type character
748+
* to wide or single-byte characters respectively.
724749
*/
725-
private string getEffectiveStringConversionChar(int n) {
726-
exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "s" or conv = "S") |
727-
(len = "l" and result = "S") or
728-
(len = "w" and result = "S") or
729-
(len = "h" and result = "s") or
730-
(len != "l" and len != "w" and len != "h" and (result = "s" or result = "S") and (if isWideCharDefault() then result != conv else result = conv))
731-
)
732-
}
733-
734750
private Type getConversionType4(int n) {
735-
exists(string cnv, CharType t | cnv = this.getEffectiveStringConversionChar(n) |
736-
cnv="s" and t = result.(PointerType).getBaseType()
737-
and not t.isExplicitlySigned()
738-
and not t.isExplicitlyUnsigned()
739-
)
740-
}
741-
742-
private Type getConversionType5(int n) {
743-
exists(string cnv | cnv = this.getEffectiveStringConversionChar(n) |
744-
cnv="S" and
751+
exists(string len, string conv |
752+
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
745753
(
746-
result = getAFormatterWideType()
747-
or
748-
not exists(getAFormatterWideType()) and
749-
result.(PointerType).getBaseType().hasName("wchar_t")
754+
(
755+
(conv = "s" or conv = "S") and
756+
len = "h" and
757+
result.(PointerType).getBaseType() instanceof PlainCharType
758+
) or (
759+
(conv = "s" or conv = "S") and
760+
(len = "l" or len = "w") and
761+
result.(PointerType).getBaseType() = getWideCharType()
762+
) or (
763+
conv = "s" and
764+
(len != "l" and len != "w" and len != "h") and
765+
result.(PointerType).getBaseType() = getDefaultCharType()
766+
) or (
767+
conv = "S" and
768+
(len != "l" and len != "w" and len != "h") and
769+
result.(PointerType).getBaseType() = getNonDefaultCharType()
770+
)
750771
)
751772
)
752773
}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
*/
88

99
import semmle.code.cpp.Function
10+
/**
11+
* A type that is used as a format string by any formatting function, or `wchar_t` if
12+
* there is none.
13+
*/
14+
private Type getAFormatterWideTypeOrDefault() {
15+
result = getAFormatterWideType().(PointerType).getBaseType() or
16+
(
17+
not exists(getAFormatterWideType().(PointerType).getBaseType()) and
18+
result instanceof Wchar_t
19+
)
20+
}
21+
1022
/**
1123
* A standard library function that uses a `printf`-like formatting string.
1224
*/
@@ -20,6 +32,43 @@ abstract class FormattingFunction extends Function {
2032
*/
2133
predicate isWideCharDefault() { none() }
2234

35+
/**
36+
* Gets the default character type expected for `%s` by this function. Typically
37+
* `char` or `wchar_t`.
38+
*/
39+
Type getDefaultCharType() {
40+
result = getParameter(getFormatParameterIndex()).getType().
41+
getUnderlyingType().(PointerType).getBaseType().stripTopLevelSpecifiers()
42+
}
43+
44+
/**
45+
* Gets the non-default character type expected for `%S` by this function. Typically
46+
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
47+
* which is correct for a particular function.
48+
*/
49+
Type getNonDefaultCharType() {
50+
(
51+
getDefaultCharType().getSize() = 1 and
52+
result = getAFormatterWideTypeOrDefault()
53+
) or (
54+
getDefaultCharType().getSize() > 1 and
55+
result instanceof PlainCharType
56+
)
57+
}
58+
59+
/**
60+
* Gets the wide character type for this function. This is usually `wchar_t`. On some
61+
* snapshots there may be multiple results where we can't tell which is correct for a
62+
* particular function.
63+
*/
64+
Type getWideCharType() {
65+
(
66+
result = getDefaultCharType() or
67+
result = getNonDefaultCharType()
68+
) and
69+
result.getSize() > 1
70+
}
71+
2372
/**
2473
* Gets the position at which the output parameter, if any, occurs.
2574
*/

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_mixed_byte_wprintf/WrongTypeFormatArguments.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
77
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
88
| tests.cpp:31:17:31:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
9-
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
10-
| tests.cpp:34:36:34:43 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
9+
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
10+
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
1111
| tests.cpp:38:36:38:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
1212
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_mixed_byte_wprintf/tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ void tests() {
3131
wprintf(L"%S", L"Hello"); // BAD: expecting char
3232

3333
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // BAD: expecting char16_t
34-
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD [FALSE POSITIVE]
35-
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t [NOT DETECTED]
34+
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD
35+
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t
3636

3737
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // GOOD
3838
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // BAD: expecting char

0 commit comments

Comments
 (0)