mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge pull request #1251 from zlaski-semmle/zlaski/cpp370
[CPP-370] Non-constant `format` arguments to `printf` and friends
This commit is contained in:
@@ -27,23 +27,23 @@ extern char *any_random_function(const char *);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if(argc > 1)
|
||||
printf(argv[1]); // not ok
|
||||
printf(argv[1]); // BAD
|
||||
else
|
||||
printf("No argument supplied.\n"); // ok
|
||||
printf("No argument supplied.\n"); // GOOD
|
||||
|
||||
printf(_("No argument supplied.\n")); // ok
|
||||
printf(_("No argument supplied.\n")); // GOOD
|
||||
|
||||
printf(dgettext(NULL, "No argument supplied.\n")); // ok
|
||||
printf(dgettext(NULL, "No argument supplied.\n")); // GOOD
|
||||
|
||||
printf(ngettext("One argument\n", "%d arguments\n", argc-1), argc-1); // ok
|
||||
printf(ngettext("One argument\n", "%d arguments\n", argc-1), argc-1); // GOOD
|
||||
|
||||
printf(gettext("%d arguments\n"), argc-1); // ok
|
||||
printf(any_random_function("%d arguments\n"), argc-1); // not ok
|
||||
printf(gettext("%d arguments\n"), argc-1); // GOOD
|
||||
printf(any_random_function("%d arguments\n"), argc-1); // BAD
|
||||
|
||||
// Our query can't look inside the argument to a macro, so it fails to
|
||||
// flag this call.
|
||||
// Even though `_` is mapped to `some_random_function` above,
|
||||
// the following call should not be flagged.
|
||||
printf(_(any_random_function("%d arguments\n")),
|
||||
argc-1); // not ok [NOT REPORTED]
|
||||
argc-1); // GOOD
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
| NonConstantFormat.c:30:10:30:16 | access to array | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| NonConstantFormat.c:41:9:41:27 | call to any_random_function | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:45:10:45:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:50:12:50:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:51:12:51:12 | call to _ | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:52:12:52:18 | call to gettext | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:53:12:53:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:54:12:54:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:55:12:55:17 | + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:56:12:56:18 | * ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:57:12:57:18 | & ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:58:12:58:39 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:60:10:60:35 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:63:12:63:20 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:69:12:69:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:75:12:75:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:81:12:81:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:86:12:86:18 | ++ ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:93:12:93:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:100:12:100:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:103:12:103:24 | new[] | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:108:12:108:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:117:10:117:19 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:48:10:48:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:54:12:54:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:57:12:57:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:58:12:58:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:59:12:59:17 | + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:60:12:60:18 | * ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:61:12:61:18 | & ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:62:12:62:39 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:64:10:64:35 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:67:12:67:20 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:73:12:73:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:79:12:79:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:85:12:85:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:90:12:90:18 | ++ ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:107:12:107:24 | new[] | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
typedef void *va_list;
|
||||
#define va_start(ap, parmN)
|
||||
#define va_end(ap)
|
||||
#define va_arg(ap, type) ((type)0)
|
||||
#define NULL 0
|
||||
|
||||
extern "C" int printf(const char *fmt, ...);
|
||||
extern "C" int snprint(char *buf, int len, const char *fmt, ...);
|
||||
extern "C" int _vsnprintf_s(
|
||||
char *buffer,
|
||||
int sizeOfBuffer,
|
||||
int count,
|
||||
const char *fmt,
|
||||
va_list argptr
|
||||
);
|
||||
extern "C" int snprintf ( char * s, int n, const char * format, ... );
|
||||
|
||||
struct A {
|
||||
void do_print(const char *fmt0) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, fmt0); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
};
|
||||
|
||||
struct B {
|
||||
A a;
|
||||
void do_printing(const char *fmt) {
|
||||
a.do_print(fmt);
|
||||
}
|
||||
};
|
||||
|
||||
struct C {
|
||||
B b;
|
||||
void do_some_printing(const char *fmt) {
|
||||
b.do_printing(fmt);
|
||||
}
|
||||
const char *ext_fmt_str(void);
|
||||
};
|
||||
|
||||
void foo(void) {
|
||||
C c;
|
||||
c.do_some_printing(c.ext_fmt_str()); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
struct some_class {
|
||||
// Retrieve some target specific output strings
|
||||
virtual const char * get_fmt() const = 0;
|
||||
};
|
||||
|
||||
struct debug_ {
|
||||
int
|
||||
out_str(
|
||||
const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
char str[4096];
|
||||
int length = _vsnprintf_s(str, sizeof(str), 0, fmt, args); // GOOD
|
||||
if (length > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
some_class* some_instance = NULL;
|
||||
debug_ *debug_ctrl;
|
||||
|
||||
void diagnostic(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
debug_ctrl->out_str(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void bar(void) {
|
||||
diagnostic (some_instance->get_fmt()); // BAD
|
||||
}
|
||||
|
||||
namespace ns {
|
||||
|
||||
class blab {
|
||||
void out1(void) {
|
||||
char *fmt = (char *)__builtin_alloca(10);
|
||||
diagnostic(fmt); // BAD
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const char *choose_message(unsigned int n) {
|
||||
|
||||
const char *make_message(unsigned int n) {
|
||||
static char buf[64];
|
||||
sprintf(buf, "%d tasks left\n", n);
|
||||
sprintf(buf, "%d tasks left\n", n); // ok
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -41,78 +41,103 @@ const char *const_wash(char *str) {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
printf(choose_message(argc - 1), argc - 1); // OK
|
||||
printf(make_message(argc - 1)); // NOT OK
|
||||
printf(_("Hello, World\n")); // OK
|
||||
const char *message = messages[2];
|
||||
printf(choose_message(argc - 1), argc - 1); // GOOD
|
||||
printf(messages[1]); // GOOD
|
||||
printf(message); // GOOD
|
||||
printf(make_message(argc - 1)); // BAD
|
||||
printf("Hello, World\n"); // GOOD
|
||||
printf(_("Hello, World\n")); // GOOD
|
||||
{
|
||||
char hello[] = "hello, World\n";
|
||||
hello[0] = 'H';
|
||||
printf(hello); // NOT OK
|
||||
printf(_(hello)); // NOT OK
|
||||
printf(gettext(hello)); // NOT OK
|
||||
printf(const_wash(hello)); // NOT OK
|
||||
printf((hello + 1) + 1); // NOT OK
|
||||
printf(+hello); // NOT OK
|
||||
printf(*&hello); // NOT OK
|
||||
printf(&*hello); // NOT OK
|
||||
printf((char*)(void*)+(hello+1) + 1); // NOT OK
|
||||
printf(hello); // BAD
|
||||
printf(_(hello)); // GOOD
|
||||
printf(gettext(hello)); // GOOD
|
||||
printf(const_wash(hello)); // BAD
|
||||
printf((hello + 1) + 1); // BAD
|
||||
printf(+hello); // BAD
|
||||
printf(*&hello); // BAD
|
||||
printf(&*hello); // BAD
|
||||
printf((char*)(void*)+(hello+1) + 1); // BAD
|
||||
}
|
||||
printf(("Hello, World\n" + 1) + 1); // NOT OK
|
||||
printf(("Hello, World\n" + 1) + 1); // BAD
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
printf(hello + 1); // NOT OK
|
||||
printf(hello); // OK
|
||||
printf(hello + 1); // BAD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
hello += 1;
|
||||
printf(hello); // NOT OK
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x = x + 1" syntax
|
||||
const char *hello = "Hello, World\n";
|
||||
hello = hello + 1;
|
||||
printf(hello); // NOT OK
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x++" syntax
|
||||
const char *hello = "Hello, World\n";
|
||||
hello++;
|
||||
printf(hello); // NOT OK
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "++x" as subexpression
|
||||
const char *hello = "Hello, World\n";
|
||||
printf(++hello); // NOT OK
|
||||
printf(++hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but through a pointer
|
||||
const char *hello = "Hello, World\n";
|
||||
const char **p = &hello;
|
||||
(*p)++;
|
||||
printf(hello); // NOT OK
|
||||
printf(hello); // BAD [NOT DETECTED]
|
||||
}
|
||||
{
|
||||
// Same as above block but through a C++ reference
|
||||
const char *hello = "Hello, World\n";
|
||||
const char *&p = hello;
|
||||
p++;
|
||||
printf(hello); // NOT OK
|
||||
printf(hello); // BAD [NOT DETECTED]
|
||||
}
|
||||
if (gettext_debug) {
|
||||
printf(new char[100]); // NOT OK
|
||||
printf(new char[100]); // BAD
|
||||
}
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
const char *const *p = &hello; // harmless reference to const pointer
|
||||
printf(hello); // OK [FALSE POSITIVE]
|
||||
printf(hello); // GOOD
|
||||
hello++; // modification comes after use and so does no harm
|
||||
}
|
||||
printf(argc > 2 ? "More than one\n" : _("Only one\n")); // OK
|
||||
printf(argc > 2 ? "More than one\n" : _("Only one\n")); // GOOD
|
||||
|
||||
// This false positive arises because we use const_wash in a problematic
|
||||
// place at one call site, and then the error spreads to all call sites. It
|
||||
// does not happen for "_" only because functions with the name "_" are
|
||||
// special-cased and assumed correct in the query.
|
||||
printf(const_wash("Hello, World\n")); // OK [FALSE POSITIVE]
|
||||
// This following is OK since a const literal is passed to const_wash()
|
||||
// and the taint tracker detects this.
|
||||
//
|
||||
//
|
||||
printf(const_wash("Hello, World\n")); // GOOD
|
||||
}
|
||||
|
||||
const char *simple_func(const char *str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
void another_func(void) {
|
||||
const char *message = messages[2];
|
||||
printf(simple_func("Hello, World\n")); // GOOD
|
||||
printf(messages[1]); // GOOD
|
||||
printf(message); // GOOD
|
||||
printf("Hello, World\n"); // GOOD
|
||||
printf(gettext("Hello, World\n")); // GOOD
|
||||
}
|
||||
|
||||
void set_value_of(int *i);
|
||||
|
||||
void print_ith_message() {
|
||||
int i;
|
||||
set_value_of(&i);
|
||||
printf(messages[i], 1U); // GOOD
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
| consts.cpp:63:9:63:10 | c5 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:69:9:69:10 | c6 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:81:9:81:10 | c8 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:86:9:86:10 | v1 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:91:9:91:10 | v2 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
|
||||
@@ -59,12 +59,12 @@ void a() {
|
||||
|
||||
// GOOD: constFunc() always returns a constant string
|
||||
// But we still don't track constantness flow from functions to variables
|
||||
char *c5 = constFunc();
|
||||
char *c5 = constFunc();
|
||||
printf(c5);
|
||||
|
||||
// GOOD: constFunc() always returns a constant string
|
||||
// But we still don't track constantness flow from functions to variables
|
||||
char *c6;
|
||||
char *c6;
|
||||
c6 = constFunc();
|
||||
printf(c6);
|
||||
|
||||
@@ -81,7 +81,7 @@ void a() {
|
||||
printf(c8);
|
||||
|
||||
// BAD: v1 value came from the user
|
||||
char *v1;
|
||||
char v1[100];
|
||||
gets(v1);
|
||||
printf(v1);
|
||||
|
||||
@@ -125,7 +125,7 @@ void a() {
|
||||
// BAD: nonConstFuncToArray() always returns a value from gv1, which is started as constant but was changed to a value that came from the user
|
||||
printf(nonConstFuncToArray(0));
|
||||
|
||||
// BAD: v9 value is copied from v1, which came from the user [NOT DETECTED]
|
||||
// BAD: v9 value is copied from v1, which came from the user
|
||||
const char *v9 = v1;
|
||||
printf(v9);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user