int source(); void sink(...); void arithAssignments(int source1, int clean1) { sink(clean1); // clean clean1 += source1; clean1 += 1; sink(clean1); // $ ast,ir clean1 = source1 = 1; sink(clean1); // clean source1 = clean1 = source(); source1++; ++source1; source1 += 1; sink(source1); // $ ast,ir sink(++source1); // $ ast,ir } // --- globals --- int increment(int x) {return x + 1;} int zero(int x) {return 0;} int global1 = 0; int global2 = source(); int global3 = global2 + 1; int global4 = increment(source()); int global5 = zero(source()); int global6, global7, global8, global9, global10; void do_source() { global6 = 0; global7 = source(); global8 = global7 + 1; global9 = increment(source()); global10 = zero(source()); sink(global6); sink(global7); // $ ast,ir sink(global8); // $ ast,ir sink(global9); // $ ast,ir sink(global10); } void do_sink() { sink(global1); sink(global2); // $ ir MISSING: ast sink(global3); // $ ir MISSING: ast sink(global4); // $ ir MISSING: ast sink(global5); sink(global6); sink(global7); // $ ir MISSING: ast sink(global8); // $ ir MISSING: ast sink(global9); // $ ir MISSING: ast sink(global10); } void global_test() { do_source(); do_sink(); } // --- class fields --- class MyClass { public: MyClass() : a(0), b(source()) { c = source(); d = 0; } void myMethod() { d = source(); } int a, b, c, d; }; void class_field_test() { MyClass mc1, mc2; mc1.myMethod(); sink(mc1.a); sink(mc1.b); // $ ast,ir sink(mc1.c); // $ ast,ir sink(mc1.d); // $ ast,ir sink(mc2.a); sink(mc2.b); // $ ast,ir sink(mc2.c); // $ ast,ir sink(mc2.d); } // --- arrays --- void array_test(int i) { int arr1[10] = {0}; int arr2[10] = {0}; int arr3[10] = {0}; arr1[5] = source(); arr2[i] = source(); arr3[5] = 0; sink(arr1[5]); // $ ast,ir sink(arr1[i]); // $ ast,ir sink(arr2[5]); // $ ast,ir sink(arr2[i]); // $ ast,ir sink(arr3[5]); sink(arr3[i]); } // --- pointers --- void pointer_test() { int t1 = source(); int t2 = 1; int t3 = 1; int *p1 = &t1; int *p2 = &t2; int *p3 = &t3; *p2 = source(); sink(*p1); // $ ast,ir sink(*p2); // $ ast,ir sink(*p3); p3 = &t1; sink(*p3); // $ ast,ir *p3 = 0; sink(*p3); // $ SPURIOUS: ast } // --- return values --- int select(int i, int a, int b) { if (i == 1) { return a; } else { return b; } } void fn_test(int i) { sink(select(i, 1, source())); // $ ast,ir } // --- strings --- char *strcpy(char *destination, const char *source); char *strcat(char *destination, const char *source); namespace strings { char *source(); // char* source void strings_test1() { char *tainted = source(); char buffer[1024] = {0}; sink(source()); // $ ast,ir sink(tainted); // $ ast,ir strcpy(buffer, "Hello, "); sink(buffer); strcat(buffer, tainted); sink(buffer); // $ ast,ir } } // --- pass by reference --- namespace refs { void callee(int *p) { sink(*p); // $ ast,ir } void caller() { int x = source(); callee(&x); } } void *memcpy(void *dest, void *src, int len); void test_memcpy(int *source) { int x; memcpy(&x, source, sizeof(int)); sink(x); // $ ast=192:23 ir SPURIOUS: ast=193:6 } // --- std::swap --- #include "swap.h" void test_swap() { int x, y; x = source(); y = 0; sink(x); // $ ast,ir sink(y); // clean std::swap(x, y); sink(x); // $ SPURIOUS: ast sink(y); // $ ast,ir } // --- lambdas --- void test_lambdas() { int t = source(); int u = 0; int v = 0; int w = 0; auto a = [t, u]() -> int { sink(t); // $ ast,ir sink(u); // clean return t; }; sink(a()); // $ ast,ir auto b = [&] { sink(t); // $ ast,ir sink(u); // clean v = source(); // (v is reference captured) }; b(); sink(v); // $ MISSING: ast,ir auto c = [=] { sink(t); // $ ast,ir sink(u); // clean }; c(); auto d = [](int a, int b) { sink(a); // $ ast,ir sink(b); // clean }; d(t, u); auto e = [](int &a, int &b, int &c) { sink(a); // $ ast,ir sink(b); // clean c = source(); }; e(t, u, w); sink(w); // $ ast,ir } // --- taint through return value --- int id(int x) { return x; } void test_return() { int x, y, z, t; t = source(); x = 0; y = 0; z = 0; sink(t); // $ ast,ir sink(x); sink(y); sink(z); x = id(t); y = id(id(t)); z = id(z); sink(t); // $ ast,ir sink(x); // $ ast,ir sink(y); // $ ast,ir sink(z); } // --- taint through parameters --- void myAssign1(int &a, int &b) { a = b; } void myAssign2(int &a, int b) { a = b; } void myAssign3(int *a, int b) { *a = b; } void myAssign4(int *a, int b) { int c; c = b + 1; *a = c; } void myNotAssign(int &a, int &b) { a = a + 1; b = b + 1; } void test_outparams() { int t, a, b, c, d, e; t = source(); a = 0; b = 0; c = 0; d = 0; e = 0; sink(t); // $ ast,ir sink(a); sink(b); sink(c); sink(d); sink(e); myAssign1(a, t); myAssign2(b, t); myAssign3(&c, t); myAssign4(&d, t); myNotAssign(e, t); sink(t); // $ ast,ir sink(a); // $ ast,ir sink(b); // $ ast,ir sink(c); // $ ast,ir sink(d); // $ ast,ir sink(e); } // --- strdup --- typedef unsigned long size_t; char *strdup(const char *s1); char *strndup(const char *s1, size_t n); wchar_t* wcsdup(const wchar_t* s1); char *strdupa(const char *s1); char *strndupa(const char *s1, size_t n); void test_strdup(char *source) { char *a, *b, *c; a = strdup(source); b = strdup("hello, world"); c = strndup(source, 100); sink(a); // $ ast,ir sink(b); sink(c); // $ ast,ir } void test_strndup(int source) { char *a; a = strndup("hello, world", source); sink(a); // $ ast,ir } void test_wcsdup(wchar_t *source) { wchar_t *a, *b; a = wcsdup(source); b = wcsdup(L"hello, world"); sink(a); // $ ast,ir sink(b); } void test_strdupa(char *source) { char *a, *b, *c; a = strdupa(source); b = strdupa("hello, world"); c = strndupa(source, 100); sink(a); // $ ast,ir sink(b); sink(c); // $ ast,ir } void test_strndupa(int source) { char *a; a = strndupa("hello, world", source); sink(a); // $ ast,ir } // --- qualifiers --- class MyClass2 { public: MyClass2(int value); void setMember(int value); int getMember(); int member; }; class MyClass3 { public: MyClass3(const char *string); void setString(const char *string); const char *getString(); const char *buffer; }; void test_qualifiers() { MyClass2 a(0), b(0), *c; MyClass3 d(""); sink(a); sink(a.getMember()); a.setMember(source()); sink(a); // $ ast,ir sink(a.getMember()); // $ ast,ir sink(b); sink(b.getMember()); b.member = source(); sink(b); // $ ir MISSING: ast sink(b.member); // $ ast,ir sink(b.getMember()); // $ MISSING: ir ast c = new MyClass2(0); sink(c); sink(c->getMember()); c->setMember(source()); sink(c); // $ ast,ir sink(c->getMember()); // $ ast,ir delete c; sink(d); sink(d.getString()); d.setString(strings::source()); sink(d); // $ ast,ir sink(d.getString()); // $ ast,ir } // --- non-standard swap --- void swop(int &a, int &b) { int c = a; a = b; b = c; } void test_swop() { int x, y; x = source(); y = 0; sink(x); // $ ast,ir sink(y); // clean swop(x, y); sink(x); // $ SPURIOUS: ast,ir sink(y); // $ ast,ir } // --- getdelim --- struct FILE; int getdelim(char ** lineptr, size_t * n, int delimiter, FILE *stream); void test_getdelim(FILE* source1) { char* line = nullptr; size_t n; getdelim(&line, &n, '\n', source1); sink(line); // $ ir,ast } // --- strtok --- char *strtok(char *str, const char *delim); void test_strtok(char *source) { const char* delim = ",.-;:_"; char* tokenized = strtok(source, delim); sink(tokenized); // $ ast,ir sink(delim); } // --- strset --- char *_strset(char *str, int c); void test_strset_1(char* ptr, char source) { _strset(ptr, source); sink(ptr); // $ SPURIOUS: ast,ir sink(*ptr); // $ ast,ir } void test_strset_2(char* source) { _strset(source, 0); sink(source); // $ ast,ir } // --- mempcpy --- void *mempcpy(void *dest, const void *src, size_t n); void test_mempcpy(int *source) { int x; mempcpy(&x, source, sizeof(int)); sink(x); // $ ast=540:24 ir SPURIOUS: ast=541:6 } // --- memccpy --- void *memccpy(void *dest, const void *src, int c, size_t n); void test_memccpy(int *source) { int dest[16]; memccpy(dest, source, 42, sizeof(dest)); sink(dest); // $ ast=550:24 ir SPURIOUS: ast=551:6 } // --- strcat and related functions --- char* strcat (char*, const char*); void test_strcat(char* dest1, char* dest2, char* clean, char* source) { strcat(dest1, source); sink(dest1); // $ ast,ir strcat(dest2, clean); sink(dest2); } typedef void* _locale_t; unsigned char *_mbsncat_l(unsigned char *, const unsigned char *, int, _locale_t); void test__mbsncat_l(unsigned char* dest1, unsigned const char* ptr, unsigned char* dest3, _locale_t clean, _locale_t source, int n) { unsigned char* dest2 = _mbsncat_l(dest1, ptr, n, source); sink(dest1); // $ SPURIOUS: ast,ir sink(*dest1); // $ ast,ir sink(dest2); // $ SPURIOUS: ast,ir sink(*dest2); // $ ast,ir unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean); sink(dest3); sink(*dest3); sink(dest4); sink(*dest4); } // --- strsep --- char *strsep(char**, const char *); void test_strsep(char *source) { const char* delim = ",.-;:_"; char* tokenized; while(tokenized = strsep(&source, delim)) { sink(tokenized); // $ ast,ir sink(*tokenized); // $ ast,ir } } // --- _strinc and related functions --- char* _strinc(const char*, _locale_t); unsigned char* _mbsinc(const unsigned char*); unsigned char *_strdec(const unsigned char*, const unsigned char*); void test__strinc(char* source, char* clean, char* dest1, char* dest2, _locale_t locale) { dest1 = _strinc(source, locale); sink(dest1); // $ ast,ir sink(*dest1); // $ ast,ir dest2 = _strinc(clean, locale); sink(dest2); sink(*dest2); } void test__mbsinc(unsigned char* source_unsigned, char* source, unsigned char* dest_unsigned, char* dest) { dest_unsigned = _mbsinc(source_unsigned); sink(dest_unsigned); // $ ast,ir sink(*dest_unsigned); // $ ast,ir dest = (char*)_mbsinc((unsigned char*)source); sink(dest); // $ ast,ir sink(*dest); // $ ast,ir } void test__strdec(const unsigned char* source, unsigned char* clean, unsigned char* dest1, unsigned char* dest2, unsigned char* dest3) { dest1 = _strdec(source + 12, source); sink(dest1); // $ ast,ir sink(*dest1); // $ ast,ir // If `clean` does not precede `source` this technically breaks the precondition of _strdec. // We would still like to have taint, though. dest2 = _strdec(clean, source); sink(dest2); // $ ast,ir sink(*dest2); // $ ast,ir // Also breaks the precondition on _strdec. dest3 = _strdec(source, clean); sink(dest3); // $ ast,ir sink(*dest3); // $ ast,ir } // --- strnextc --- unsigned int _strnextc(const char*); void test__strnextc(const char* source) { unsigned c = 0; do { c = _strnextc(source++); sink(c); // $ ast,ir } while(c != '\0'); c = _strnextc(""); sink(c); } // --- taint through const specified function --- class C_no_const_member_function { char* data_; public: char* data() { return data_; } }; void test_no_const_member(char* source) { C_no_const_member_function c; memcpy(c.data(), source, 16); sink(c.data()); // $ ast,ir } class C_const_member_function { char* data_; public: char* data() const { return data_; } }; void test_with_const_member(char* source) { C_const_member_function c; memcpy(c.data(), source, 16); sink(c.data()); // $ ast,ir } void argument_source(void*); struct two_members { char *x, *y; }; void test_argument_source_field_to_obj() { two_members s; argument_source(s.x); sink(s); // $ SPURIOUS: ast,ir sink(s.x); // $ ast,ir sink(s.y); // clean } namespace strings { void test_write_to_read_then_incr_then_deref() { char* s = source(); char* p; *p++ = *s; sink(p); // $ ast ir } } char * strncpy (char *, const char *, unsigned long); void test_strncpy(char* d, char* s) { argument_source(s); strncpy(d, s, 16); sink(d); // $ ast ir } char* indirect_source(); void test_strtok_indirect() { char *source = indirect_source(); const char* delim = ",.-;:_"; char* tokenized = strtok(source, delim); sink(*tokenized); // $ ir MISSING: ast sink(*delim); } long int strtol(const char*, char**, int); void test_strtol(char *source) { char* endptr = nullptr; long l = strtol(source, &endptr, 10); sink(l); // $ ast,ir sink(endptr); // $ ast,ir sink(*endptr); // $ ast,ir } void *malloc(size_t); void *realloc(void *, size_t); void test_realloc() { char *source = indirect_source(); char *dest = (char*)realloc(source, 16); sink(dest); // $ ir MISSING: ast } void test_realloc_2_indirections(int **buffer) { **buffer = source(); buffer = (int**)realloc(buffer, 16); sink(**buffer); // $ ir MISSING: ast } void test_realloc_struct_field() { struct A { int x; }; A* a = (A*)malloc(sizeof(A)); a->x = source(); A* a2 = (A*)realloc(a, sizeof(A)); sink(a2->x); // $ ir MISSING: ast } int sprintf(char *, const char *, ...); void call_sprintf_twice(char* path, char* data) { sprintf(path, "%s", "abc"); sprintf(path, "%s", data); } void test_call_sprintf() { char path[10]; call_sprintf_twice(path, indirect_source()); sink(*path); // $ ast,ir } struct TaintInheritingContentObject { int flowFromObject; }; TaintInheritingContentObject source(bool); void test_TaintInheritingContent() { TaintInheritingContentObject obj = source(true); sink(obj.flowFromObject); // $ ir MISSING: ast } FILE* fopen(const char*, const char*); int fopen_s(FILE** pFile, const char *filename, const char *mode); void fopen_test(char* source) { FILE* f = fopen(source, "r"); sink(f); // $ ast,ir FILE* f2; fopen_s(&f2, source, "r"); sink(f2); // $ ast,ir } typedef wchar_t OLECHAR; typedef OLECHAR* LPOLESTR; typedef const LPOLESTR LPCOLESTR; typedef OLECHAR* BSTR; typedef const char* LPCSTR; BSTR SysAllocString(const OLECHAR *); BSTR SysAllocStringByteLen(LPCSTR, unsigned ); BSTR SysAllocStringLen(const OLECHAR *,unsigned); void test_sysalloc() { auto p1 = SysAllocString((LPOLESTR)indirect_source()); sink(*p1); // $ ir MISSING: ast auto p2 = SysAllocStringByteLen(indirect_source(), 10); sink(*p2); // $ ir MISSING: ast auto p3 = SysAllocStringLen((LPOLESTR)indirect_source(), 10); sink(*p3); // $ ir MISSING: ast } char* strchr(const char*, int); void write_to_const_ptr_ptr(const char **p_out, const char **p_in) { const char* q = *p_in; *p_out = strchr(q, '/'); } void take_const_ptr(const char *out, const char *in) { // NOTE: We take the address of `out` in `take_const_ptr`'s stack space. // Assigning to this pointer does not change `out` in // `test_write_to_const_ptr_ptr`. write_to_const_ptr_ptr(&out, &in); } void test_write_to_const_ptr_ptr() { const char* in = indirect_source(); const char* out; take_const_ptr(out, in); sink(out); // $ SPURIOUS: ast }