Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 5 additions & 3 deletions cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ private predicate formattingFunctionCallExpectedType(FormattingFunctionCall ffc,
ffc.getTarget() = f and
f.getFormatParameterIndex() = i and
ffc.getArgument(i) = fl and
fl.getConversionType(pos) = expected and
count(fl.getConversionType(pos)) = 1
fl.getConversionType(pos) = expected
)
}

Expand Down Expand Up @@ -143,7 +142,10 @@ from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
where (
(
formatArgType(ffc, n, expected, arg, actual) and
not trivialConversion(expected.getUnspecifiedType(), actual.getUnspecifiedType())
not exists(Type anyExpected |
formatArgType(ffc, n, anyExpected, arg, actual) and
trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType())
)
)
or
(
Expand Down
4 changes: 2 additions & 2 deletions cpp/ql/src/semmle/code/cpp/commons/Printf.qll
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ class FormatLiteral extends Literal {
or ((len="z" or len="Z")
and (result = this.getSize_t() or result = this.getSsize_t()))
or (len="t" and result = this.getPtrdiff_t())
or (len="I" and result instanceof IntType)
or (len="I" and (result = this.getSize_t() or result = this.getPtrdiff_t()))
or (len="I32" and exists(MicrosoftInt32Type t |
t.getUnsigned() = result.(IntegralType).getUnsigned()
))
Expand All @@ -604,7 +604,7 @@ class FormatLiteral extends Literal {
or ((len="z" or len="Z")
and (result = this.getSize_t() or result = this.getSsize_t()))
or (len="t" and result = this.getPtrdiff_t())
or (len="I" and result instanceof IntType)
or (len="I" and (result = this.getSize_t() or result = this.getPtrdiff_t()))
or (len="I32" and exists(MicrosoftInt32Type t |
t.getUnsigned() = result.(IntegralType).getUnsigned()
))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
| 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:21:15:21:21 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
| tests.cpp:21:15:21:21 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know how this will render in LGTM? One message with two values, or two separate messages on the same line?

Copy link
Contributor

Choose a reason for hiding this comment

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

On LGTM it'll be one alert with an expandable placeholder that says [ 2 values ]. Here's an example from a Java query.

| 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:29:17:29:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void tests() {
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", "Hello"); // BAD: expecting wchar_t or char16_t
printf("%S", u"Hello"); // GOOD
printf("%S", L"Hello"); // GOOD

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
| tests_32.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' |
| tests_32.cpp:15:15:15:15 | l | This argument should be of type 'void *' but is of type 'long' |
| tests_64.cpp:14:16:14:23 | void_ptr | This argument should be of type 'long' but is of type 'void *' |
| tests_64.cpp:15:15:15:15 | l | This argument should be of type 'void *' but is of type 'long' |
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ void test_32()

printf("%li", l); // GOOD
printf("%li", void_ptr); // BAD
printf("%p", l); // BAD [NOT DETECTED]
printf("%p", l); // BAD
printf("%p", void_ptr); // GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ void test_64()

printf("%li", l); // GOOD
printf("%li", void_ptr); // BAD
printf("%p", l); // BAD [NOT DETECTED]
printf("%p", l); // BAD
printf("%p", void_ptr); // GOOD
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
| format.h:16:59:16:61 | str | This argument should be of type 'int' but is of type 'char *' |
| format.h:16:64:16:64 | i | This argument should be of type 'double' but is of type 'int' |
| format.h:16:67:16:67 | d | This argument should be of type 'char *' but is of type 'double' |
| linux_c.c:11:15:11:18 | str3 | This argument should be of type 'char *' but is of type 'short *' |
| pri_macros.h:15:35:15:40 | my_u64 | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:12:27:12:27 | i | This argument should be of type 'double' but is of type 'int' |
| printf1.h:18:18:18:18 | i | This argument should be of type 'void *' but is of type 'int' |
Expand All @@ -12,6 +13,8 @@
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:113:17:113:17 | d | This argument should be of type 'long double' but is of type 'double' |
| printf1.h:114:18:114:18 | d | This argument should be of type 'long double' but is of type 'double' |
| printf1.h:147:19:147:19 | i | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:148:19:148:20 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| 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 *' |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** standard printf functions */

int printf(const char *format, ...);

/** test program */

void restrict_cases(char * restrict str1, const char * restrict str2, short * restrict str3)
{
printf("%s", str1); // GOOD
printf("%s", str2); // GOOD
printf("%s", str3); // BAD
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
semmle-extractor-options: --edg --signed_chars
semmle-extractor-options: --clang --edg --signed_chars
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,20 @@ void extensions()
printf("%llu", uli); // BAD (should be %lu) [NOT DETECTED]
}
}

void fun4()
{
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;

printf("%qi\n", i); // BAD
printf("%qu\n", ui); // BAD
printf("%qi\n", l); // GOOD
printf("%qu\n", ul); // GOOD
printf("%qi\n", ll); // GOOD
printf("%qu\n", ull); // GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
| printf1.h:44:18:44:20 | ull | This argument should be of type 'int' but is of type 'unsigned long long' |
| 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: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 *' |
| printf1.h:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
| printf1.h:154:18:154:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
| printf1.h:155:18:155:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
| printf1.h:168:19:168:19 | i | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:169:19:169:20 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| 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 *' |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ void fun1(unsigned char* a, unsigned char* b) {
printf("%td\n", a-b); // GOOD
}

typedef wchar_t WCHAR_T; // WCHAR_T -> wchar_t -> int
typedef int MYCHAR; // MYCHAR -> int (notably not via the wchar_t typedef)

void fun2() {
wchar_t *myString1;
WCHAR_T *myString2;
int *myString3;
MYCHAR *myString4;

printf("%S", myString1); // GOOD
printf("%S", myString2); // GOOD
printf("%S", myString3); // GOOD
printf("%S", myString4); // GOOD
}

typedef void *VOIDPTR;
typedef int (*FUNPTR)(int);

void fun3(void *p1, VOIDPTR p2, FUNPTR p3, char *p4)
{
printf("%p\n", p1); // GOOD
printf("%p\n", p2); // GOOD
printf("%p\n", p3); // GOOD
printf("%p\n", p4); // GOOD
printf("%p\n", p4 + 1); // GOOD
printf("%p\n", 0); // GOOD [FALSE POSITIVE]
}

typedef unsigned int wint_t;

void test_chars(char c, wchar_t wc, wint_t wt)
Expand All @@ -127,3 +155,20 @@ void test_ws(char *c, wchar_t *wc)
wprintf(L"%S", c); // BAD
wprintf(L"%S", wc); // GOOD
}

void fun4()
{
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;

printf("%qi\n", i); // BAD
printf("%qu\n", ui); // BAD
printf("%qi\n", l); // GOOD
printf("%qu\n", ul); // GOOD
printf("%qi\n", ll); // GOOD
printf("%qu\n", ull); // GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,21 @@
| 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:116:16:116:24 | myString3 | This argument should be of type '__wchar_t *' but is of type 'int *' |
| printf1.h:117:16:117:24 | myString4 | This argument should be of type '__wchar_t *' but is of type 'int *' |
| printf1.h:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
| printf1.h:153:18:153:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
| printf1.h:156:18:156:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
| printf1.h:181:21:181:22 | ll | This argument should be of type 'int' but is of type 'long long' |
| printf1.h:182:21:182:23 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:185:21:185:23 | i64 | This argument should be of type 'int' but is of type 'long long' |
| printf1.h:186:21:186:23 | u64 | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:188:21:188:21 | i | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:189:21:189:22 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| printf1.h:190:21:190:21 | l | This argument should be of type 'long long' but is of type 'long' |
| printf1.h:191:21:191:22 | ul | This argument should be of type 'unsigned long long' but is of type 'unsigned long' |
| printf1.h:194:21:194:23 | i32 | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:195:21:195:23 | u32 | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| 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 *' |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ void fun1(unsigned char* a, unsigned char* b) {
printf("%td\n", a-b); // GOOD
}

typedef wchar_t WCHAR_T; // WCHAR_T -> wchar_t
typedef int MYCHAR; // MYCHAR -> int

void fun2() {
wchar_t *myString1;
WCHAR_T *myString2;
int *myString3;
MYCHAR *myString4;

printf("%S", myString1); // GOOD
printf("%S", myString2); // GOOD
printf("%S", myString3); // BAD
printf("%S", myString4); // BAD
}

typedef void *VOIDPTR;
typedef int (*FUNPTR)(int);

void fun3(void *p1, VOIDPTR p2, FUNPTR p3, char *p4)
{
printf("%p\n", p1); // GOOD
printf("%p\n", p2); // GOOD
printf("%p\n", p3); // GOOD
printf("%p\n", p4); // GOOD
printf("%p\n", p4 + 1); // GOOD
printf("%p\n", 0); // GOOD [FALSE POSITIVE]
}

typedef unsigned int wint_t;

void test_chars(char c, wchar_t wc, wint_t wt)
Expand All @@ -127,3 +155,44 @@ void test_ws(char *c, wchar_t *wc, wint_t *wt)
wprintf(L"%S", c); // GOOD
wprintf(L"%S", wc); // BAD
}

void fun4()
{
ptrdiff_t pdt;
size_t sz;
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;
__int32 i32;
unsigned __int32 u32;
__int64 i64;
unsigned __int64 u64;

printf("%Ii\n", pdt); // GOOD
printf("%Iu\n", sz); // GOOD

printf("%I32i\n", i); // GOOD
printf("%I32u\n", ui); // GOOD
printf("%I32i\n", l); // GOOD
printf("%I32u\n", ul); // GOOD
printf("%I32i\n", ll); // BAD
printf("%I32u\n", ull); // BAD
printf("%I32i\n", i32); // GOOD
printf("%I32u\n", u32); // GOOD
printf("%I32i\n", i64); // BAD
printf("%I32u\n", u64); // BAD

printf("%I64i\n", i); // BAD
printf("%I64u\n", ui); // BAD
printf("%I64i\n", l); // BAD
printf("%I64u\n", ul); // BAD
printf("%I64i\n", ll); // GOOD
printf("%I64u\n", ull); // GOOD
printf("%I64i\n", i32); // BAD
printf("%I64u\n", u32); // BAD
printf("%I64i\n", i64); // GOOD
printf("%I64u\n", u64); // GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@
| 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:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
| printf1.h:155:21:155:22 | ll | This argument should be of type 'int' but is of type 'long long' |
| printf1.h:156:21:156:23 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:159:21:159:23 | i64 | This argument should be of type 'int' but is of type 'long long' |
| printf1.h:160:21:160:23 | u64 | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
| printf1.h:162:21:162:21 | i | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:163:21:163:22 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| printf1.h:164:21:164:21 | l | This argument should be of type 'long long' but is of type 'long' |
| printf1.h:165:21:165:22 | ul | This argument should be of type 'unsigned long long' but is of type 'unsigned long' |
| printf1.h:168:21:168:23 | i32 | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:169:21:169:23 | u32 | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| 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 *' |
| real_world.h:64:22:64:24 | & ... | This argument should be of type 'short *' but is of type 'signed int *' |
| wide_string.h:25:18:25:20 | c | This argument should be of type 'char' but is of type 'char *' |
| wide_string.h:29:19:29:22 | c | This argument should be of type 'wchar_t' but is of type 'unsigned short *' |
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,44 @@ void fun3(void *p1, VOIDPTR p2, FUNPTR p3, char *p4)
printf("%p\n", p4 + 1); // GOOD
printf("%p\n", 0); // GOOD [FALSE POSITIVE]
}

void fun4()
{
ptrdiff_t pdt;
size_t sz;
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;
__int32 i32;
unsigned __int32 u32;
__int64 i64;
unsigned __int64 u64;

printf("%Ii\n", pdt); // GOOD
printf("%Iu\n", sz); // GOOD

printf("%I32i\n", i); // GOOD
printf("%I32u\n", ui); // GOOD
printf("%I32i\n", l); // GOOD
printf("%I32u\n", ul); // GOOD
printf("%I32i\n", ll); // BAD
printf("%I32u\n", ull); // BAD
printf("%I32i\n", i32); // GOOD
printf("%I32u\n", u32); // GOOD
printf("%I32i\n", i64); // BAD
printf("%I32u\n", u64); // BAD

printf("%I64i\n", i); // BAD
printf("%I64u\n", ui); // BAD
printf("%I64i\n", l); // BAD
printf("%I64u\n", ul); // BAD
printf("%I64i\n", ll); // GOOD
printf("%I64u\n", ull); // GOOD
printf("%I64i\n", i32); // BAD
printf("%I64u\n", u32); // BAD
printf("%I64i\n", i64); // GOOD
printf("%I64u\n", u64); // GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ void test_wchar4(char c, const char cc, wchar_t wc, const wchar_t wcc) {
printf("%wc", wc); // GOOD
printf("%wc", wcc); // GOOD
printf("%wc", L'c'); // GOOD
printf("%wc", L"c"); // BAD [NOT DETECTED]
printf("%wc", L"c"); // BAD
}