mirror of
https://github.com/github/codeql.git
synced 2025-12-17 17:23:36 +01:00
429 lines
7.9 KiB
C++
429 lines
7.9 KiB
C++
void *malloc(int);
|
|
void free(void *);
|
|
bool condition();
|
|
void use(void*);
|
|
void *realloc(void*, unsigned long);
|
|
int strlen(char*);
|
|
int asprintf(char ** strp, const char * fmt, ...);
|
|
|
|
|
|
void* test_double_free1(int *a) {
|
|
free(a); // GOOD
|
|
a[5] = 5; // BAD
|
|
*a = 5; // BAD
|
|
free(a); // BAD
|
|
a = (int*) malloc(8);
|
|
free(a); // GOOD
|
|
a = (int*) malloc(8);
|
|
if (!a) free(a);
|
|
return a;
|
|
}
|
|
|
|
void test_double_free_aliasing(void *a, void* b) {
|
|
free(a); // GOOD
|
|
a = b;
|
|
free(a); // GOOD
|
|
free(b); // BAD [NOT DETECTED]
|
|
}
|
|
|
|
void test_dominance1(void *a) {
|
|
free(a);
|
|
if (condition()) free(a); // BAD
|
|
}
|
|
|
|
void test_dominance2(void *a) {
|
|
free(a);
|
|
if (condition()) a = malloc(10);
|
|
if (condition()) free(a); // BAD
|
|
}
|
|
|
|
void test_post_dominance1(int *a)
|
|
{
|
|
if (condition()) free(a);
|
|
if (condition()) a[2] = 5; // BAD [NOT DETECTED]
|
|
if (condition()) free(a); // BAD [NOT DETECTED]
|
|
a[2] = 5; // BAD
|
|
free(a); // BAD
|
|
}
|
|
|
|
void test_post_dominance2(void *a) {
|
|
if (condition()) free(a);
|
|
free(a); // BAD
|
|
}
|
|
|
|
void test_post_dominance3(void *a) {
|
|
if (condition()) free(a);
|
|
a = malloc(10);
|
|
free(a); // GOOD
|
|
}
|
|
|
|
void test_use_after_free6(int *a, int *b) {
|
|
free(a);
|
|
a=b;
|
|
free(b);
|
|
if (condition()) a[0] = 5; // BAD [NOT DETECTED]
|
|
}
|
|
|
|
void test_use_after_free7(int *a) {
|
|
a[0] = 42;
|
|
free(a);
|
|
|
|
if (a[3]) { // BAD
|
|
free(a); // BAD
|
|
}
|
|
}
|
|
|
|
class A {
|
|
public:
|
|
void f();
|
|
};
|
|
|
|
void test_new1() {
|
|
A *a = new A();
|
|
delete(a);
|
|
a->f(); // BAD
|
|
delete(a); // BAD
|
|
}
|
|
|
|
void test_dereference1(A *a) {
|
|
a->f(); // GOOD
|
|
free(a);
|
|
a->f(); // BAD
|
|
}
|
|
|
|
void* use_after_free(void *a) {
|
|
free(a);
|
|
use(a); // BAD
|
|
return a; // BAD
|
|
}
|
|
|
|
void test_realloc1(void *a) {
|
|
free(a);
|
|
void *b = realloc(a, sizeof(a)*3); // BAD [NOT DETECTED by cpp/double-free]
|
|
free(a); // BAD
|
|
free(b); // GOOD
|
|
}
|
|
void* test_realloc2(char *a) {
|
|
void *b = realloc(a, strlen(a)+3); // GOOD
|
|
|
|
// From the man page on return values from realloc and reallocarray:
|
|
// "If these functions fail, the original block is left untouched; it is not freed or moved."
|
|
if (!b) {
|
|
free(a); // GOOD
|
|
}
|
|
free(b); // GOOD
|
|
}
|
|
|
|
void test_realloc3(void *a) {
|
|
void *b = realloc(a, 100);
|
|
if (b) free(b); // GOOD
|
|
if (!b) {
|
|
free(a); // GOOD
|
|
}
|
|
}
|
|
|
|
void test_ptr_deref(void ** a) {
|
|
free(*a);
|
|
*a = malloc(10);
|
|
free(*a); // GOOD
|
|
free(*a); // BAD
|
|
*a = malloc(10);
|
|
free(a[0]); // GOOD
|
|
free(a[1]); // GOOD
|
|
}
|
|
|
|
struct list {
|
|
struct list *next;
|
|
void* data;
|
|
};
|
|
|
|
void test_loop1(struct list ** list_ptr) {
|
|
struct list *next;
|
|
while (*list_ptr) { // GOOD
|
|
free((*list_ptr)->data); // GOOD
|
|
next = (*list_ptr)->next; // GOOD
|
|
free(*list_ptr); // GOOD
|
|
*list_ptr = next; // GOOD
|
|
}
|
|
free(list_ptr); // GOOD
|
|
}
|
|
|
|
void test_use_after_free8(struct list * a) {
|
|
if (condition()) free(a);
|
|
a->data = malloc(10); // BAD
|
|
free(a); // BAD
|
|
}
|
|
|
|
void test_loop2(char ** a) {
|
|
while (*a) { // GOOD
|
|
free(*a); // GOOD
|
|
a++;
|
|
}
|
|
free(a); // GOOD
|
|
}
|
|
|
|
void* test_realloc4() {
|
|
void *a = 0;
|
|
void *b = realloc(a, 10); // BAD for cpp/memory-never-freed
|
|
if (!b) { return a; }
|
|
return b;
|
|
}
|
|
|
|
void test_sizeof(int *a) {
|
|
free(a);
|
|
int x = sizeof(a[0]); // GOOD
|
|
}
|
|
|
|
void call_by_reference(char * &a);
|
|
int custom_alloc_func(char ** a);
|
|
|
|
void test_reassign(char *a) {
|
|
free(a); // GOOD
|
|
asprintf(&a, "Hello world"); // GOOD
|
|
free(a); //GOOD
|
|
call_by_reference(a); // GOOD
|
|
free(a); // GOOD
|
|
int v;
|
|
if (v = custom_alloc_func(&a)) return;
|
|
free(a); // GOOD
|
|
}
|
|
|
|
char* test_return1(char *a) {
|
|
int ret = condition();
|
|
if (!ret) free(a);
|
|
return (ret ? a : 0);
|
|
}
|
|
|
|
char* test_return2(char *a) {
|
|
int ret = condition();
|
|
if (!ret) free(a);
|
|
if (ret) return a;
|
|
else return 0;
|
|
}
|
|
|
|
void test_condition1(char *a) {
|
|
free(a);
|
|
if (asprintf(&a, "Hello world") || condition());
|
|
free(a); //GOOD
|
|
if (condition() || asprintf(&a, "Hello world"));
|
|
free(a); // BAD
|
|
}
|
|
|
|
void test_condition2(char *a) {
|
|
free(a);
|
|
if (condition()) a = (char*) malloc(10);
|
|
else custom_alloc_func(&a);
|
|
free(a); // GOOD
|
|
}
|
|
|
|
void* test_return1(void *a) {
|
|
free(a);
|
|
return a;
|
|
}
|
|
|
|
void MmFreePagesFromMdl(void*);
|
|
void ExFreePool(void*);
|
|
void test_ms_free(void * memory_descriptor_list) {
|
|
MmFreePagesFromMdl(memory_descriptor_list); //GOOD
|
|
ExFreePool(memory_descriptor_list); // GOOD
|
|
}
|
|
|
|
void test_loop3(char ** a, char ** b) {
|
|
if (*a) {
|
|
free(*a);
|
|
a++;
|
|
}
|
|
use(*a); // GOOD [FALSE POSITIVE]
|
|
|
|
for (;*b; b++) {
|
|
free(*b);
|
|
}
|
|
use(*b); // GOOD [FALSE POSITIVE]
|
|
}
|
|
|
|
void test_deref(char **a) {
|
|
free(*a);
|
|
use(*a); // GOOD [FALSE POSITIVE]
|
|
}
|
|
|
|
// Refs
|
|
|
|
void test_ref(char *&p) {
|
|
free(p);
|
|
p = (char *)malloc(sizeof(char)*10);
|
|
use(p); // GOOD
|
|
free(p); // GOOD
|
|
}
|
|
|
|
|
|
void test_ref_delete(int *&p) {
|
|
delete p;
|
|
p = new int;
|
|
use(p); // GOOD
|
|
delete p; // GOOD
|
|
}
|
|
|
|
void test_free_assign() {
|
|
void *a = malloc(10);
|
|
void *b;
|
|
free(b = a); // GOOD
|
|
}
|
|
|
|
struct MyStruct {
|
|
char* buf;
|
|
};
|
|
|
|
void test_free_struct(MyStruct* s) {
|
|
free(s->buf);
|
|
char c = s->buf[0]; // BAD
|
|
}
|
|
|
|
void test_free_struct2(MyStruct s) {
|
|
free(s.buf);
|
|
char c = s.buf[0]; // BAD
|
|
}
|
|
|
|
void test_free_struct3(MyStruct s) {
|
|
char* buf = s.buf;
|
|
free(buf);
|
|
char c = s.buf[0]; // BAD [FALSE NEGATIVE]
|
|
}
|
|
|
|
void test_free_struct4(char* buf, MyStruct s) {
|
|
free(buf);
|
|
s.buf = buf;
|
|
char c = s.buf[0]; // BAD
|
|
}
|
|
|
|
void g_free (void*);
|
|
|
|
void test_g_free(char* buf) {
|
|
g_free(buf);
|
|
g_free(buf); // BAD
|
|
}
|
|
|
|
// inspired by real world FPs
|
|
|
|
void test_goto() {
|
|
int *a = (int *)malloc(sizeof(int));
|
|
|
|
*a = 1; // GOOD
|
|
if (condition())
|
|
{
|
|
delete a;
|
|
goto after;
|
|
}
|
|
*a = 1; // GOOD
|
|
if (condition())
|
|
{
|
|
delete a;
|
|
}
|
|
*a = 1; // BAD (use after free)
|
|
delete a; // BAD (double free)
|
|
after:
|
|
*a = 1; // BAD (use after free)
|
|
}
|
|
|
|
void test_reassign() {
|
|
int *a = (int *)malloc(sizeof(int));
|
|
|
|
*a = 1; // GOOD
|
|
delete a;
|
|
*a = 1; // BAD (use after free)
|
|
a = (int *)malloc(sizeof(int));
|
|
*a = 1; // GOOD
|
|
delete a;
|
|
}
|
|
|
|
struct PtrContainer {
|
|
int *ptr;
|
|
};
|
|
|
|
void test_array(PtrContainer *containers) {
|
|
delete containers[0].ptr; // GOOD
|
|
delete containers[1].ptr; // GOOD
|
|
delete containers[2].ptr; // GOOD
|
|
delete containers[2].ptr; // BAD (double free) [NOT DETECTED]
|
|
}
|
|
|
|
struct E {
|
|
struct EC {
|
|
int* a;
|
|
} ec[2];
|
|
};
|
|
|
|
void test(E* e) {
|
|
free(e->ec[0].a);
|
|
free(e->ec[1].a); // GOOD
|
|
}
|
|
|
|
// ---
|
|
|
|
void test_return_by_parameter(int **out_i, MyStruct **out_ms) {
|
|
int *a = (int *)malloc(sizeof(int)); // GOOD (freed)
|
|
int *b = (int *)malloc(sizeof(int)); // GOOD (out parameter)
|
|
int *d = (int *)malloc(sizeof(int)); // BAD (not freed)
|
|
MyStruct *e = (MyStruct *)malloc(sizeof(MyStruct)); // GOOD (freed)
|
|
MyStruct *f = (MyStruct *)malloc(sizeof(MyStruct)); // GOOD (out parameter)
|
|
MyStruct *h = (MyStruct *)malloc(sizeof(MyStruct)); // BAD (not freed)
|
|
|
|
free(a);
|
|
*out_i = b;
|
|
|
|
free(e);
|
|
*out_ms = f;
|
|
}
|
|
|
|
void test_return_by_parameter_caller() {
|
|
int *i;
|
|
MyStruct *s;
|
|
|
|
test_return_by_parameter(&i, &s);
|
|
free(i);
|
|
free(s);
|
|
}
|
|
|
|
// ---
|
|
|
|
class HasGetterAndFree {
|
|
public:
|
|
HasGetterAndFree() {
|
|
buffer = malloc(1024);
|
|
}
|
|
|
|
~HasGetterAndFree() {
|
|
free(buffer);
|
|
}
|
|
|
|
void *getBuffer() {
|
|
return buffer;
|
|
}
|
|
|
|
void *buffer;
|
|
};
|
|
|
|
class HasGetterNoFree {
|
|
public:
|
|
HasGetterNoFree() {
|
|
buffer = malloc(1024);
|
|
}
|
|
|
|
void *getBuffer() {
|
|
return buffer;
|
|
}
|
|
|
|
void *buffer;
|
|
};
|
|
|
|
void testHasGetter() {
|
|
HasGetterAndFree hg;
|
|
void *buffer = hg.getBuffer(); // GOOD (freed in destructor)
|
|
|
|
HasGetterNoFree hg2;
|
|
void *buffer2 = hg2.getBuffer(); // GOOD (freed below)
|
|
free(buffer2);
|
|
|
|
HasGetterNoFree hg3;
|
|
void *buffer3 = hg3.getBuffer(); // BAD (not freed) [NOT DETECTED]
|
|
}
|