211 lines · cpp
1// RUN: %clang_analyze_cc1 -std=c++11 -Wno-format-security \2// RUN: -analyzer-checker=core,optin.taint,security.ArrayBound,debug.ExprInspection \3// RUN: -analyzer-config optin.taint.TaintPropagation:Config=%S/Inputs/taint-generic-config.yaml \4// RUN: -verify %s5 6template <typename T> void clang_analyzer_isTainted(T);7 8#define BUFSIZE 109int Buffer[BUFSIZE];10 11int scanf(const char*, ...);12template <typename T = int> T mySource1();13int mySource3();14 15typedef struct _FILE FILE;16extern "C" {17extern FILE *stdin;18}19int fscanf(FILE *stream, const char *format, ...);20 21bool isOutOfRange2(const int*);22 23void mySink2(int);24 25// Test configuration26namespace myNamespace {27 void scanf(const char*, ...);28 void myScanf(const char*, ...);29 int mySource3();30 31 bool isOutOfRange(const int*);32 bool isOutOfRange2(const int*);33 34 void mySink(int, int, int);35 void mySink2(int);36}37 38namespace myAnotherNamespace {39 int mySource3();40 41 bool isOutOfRange2(const int*);42 43 void mySink2(int);44}45 46void testConfigurationNamespacePropagation1() {47 int x;48 // The built-in functions should be matched only for functions in49 // the global namespace50 myNamespace::scanf("%d", &x);51 Buffer[x] = 1; // no-warning52 53 scanf("%d", &x);54 Buffer[x] = 1; // expected-warning {{Potential out of bound access }}55}56 57void testConfigurationNamespacePropagation2() {58 int x = mySource3();59 Buffer[x] = 1; // no-warning60 61 int y = myNamespace::mySource3();62 Buffer[y] = 1; // expected-warning {{Potential out of bound access }}63}64 65void testConfigurationNamespacePropagation3() {66 int x = myAnotherNamespace::mySource3();67 Buffer[x] = 1; // expected-warning {{Potential out of bound access }}68}69 70void testConfigurationNamespacePropagation4() {71 int x;72 // Configured functions without scope should match for all function.73 myNamespace::myScanf("%d", &x);74 Buffer[x] = 1; // expected-warning {{Potential out of bound access }}75}76 77void testConfigurationNamespaceFilter1() {78 int x = mySource1();79 if (myNamespace::isOutOfRange2(&x))80 return;81 Buffer[x] = 1; // no-warning82 83 int y = mySource1();84 if (isOutOfRange2(&y))85 return;86 Buffer[y] = 1; // expected-warning {{Potential out of bound access }}87}88 89void testConfigurationNamespaceFilter2() {90 int x = mySource1();91 if (myAnotherNamespace::isOutOfRange2(&x))92 return;93 Buffer[x] = 1; // no-warning94}95 96void testConfigurationNamespaceFilter3() {97 int x = mySource1();98 if (myNamespace::isOutOfRange(&x))99 return;100 Buffer[x] = 1; // no-warning101}102 103void testConfigurationNamespaceSink1() {104 int x = mySource1();105 mySink2(x); // no-warning106 107 int y = mySource1();108 myNamespace::mySink2(y);109 // expected-warning@-1 {{Untrusted data is passed to a user-defined sink}}110}111 112void testConfigurationNamespaceSink2() {113 int x = mySource1();114 myAnotherNamespace::mySink2(x);115 // expected-warning@-1 {{Untrusted data is passed to a user-defined sink}}116}117 118void testConfigurationNamespaceSink3() {119 int x = mySource1();120 myNamespace::mySink(x, 0, 1);121 // expected-warning@-1 {{Untrusted data is passed to a user-defined sink}}122}123 124struct Foo {125 void scanf(const char*, int*);126 void myMemberScanf(const char*, int*);127};128 129void testConfigurationMemberFunc() {130 int x;131 Foo foo;132 foo.scanf("%d", &x);133 Buffer[x] = 1; // no-warning134 135 foo.myMemberScanf("%d", &x);136 Buffer[x] = 1; // expected-warning {{Potential out of bound access }}137}138 139void testReadingFromStdin(char **p) {140 int n;141 fscanf(stdin, "%d", &n);142 Buffer[n] = 1; // expected-warning {{Potential out of bound access }}143}144 145namespace gh114270 {146class Empty {};147class Aggr {148public:149 int data;150};151 152void top() {153 int Int = mySource1<int>();154 clang_analyzer_isTainted(Int); // expected-warning {{YES}}155 156 // It's fine to not propagate taint to empty classes, since they don't have any data members.157 Empty E = mySource1<Empty>();158 clang_analyzer_isTainted(E); // expected-warning {{NO}}159 160 Aggr A = mySource1<Aggr>();161 // FIXME Ideally, both A and A.data should be tainted. However, the162 // implementation used by e5ac9145ba29 ([analyzer][taint] Recognize163 // tainted LazyCompoundVals (4/4) (#115919), 2024-11-15) led to FPs and164 // FNs in various scenarios and had to be reverted to fix #153782.165 clang_analyzer_isTainted(A); // expected-warning {{NO}}166 clang_analyzer_isTainted(A.data); // expected-warning {{YES}}167}168} // namespace gh114270169 170 171namespace format_attribute {172__attribute__((__format__ (__printf__, 1, 2)))173void log_freefunc(const char *fmt, ...);174 175void test_format_attribute_freefunc() {176 int n;177 fscanf(stdin, "%d", &n); // Get a tainted value.178 179 log_freefunc("This number is suspicious: %d\n", n); // no-warning180}181 182struct Foo {183 // When the format attribute is applied to a method, argumet '1' is the184 // implicit `this`, so e.g. in this case argument '2' specifies `fmt`.185 // Specifying '1' instead of '2' would produce a compilation error:186 // "format attribute cannot specify the implicit this argument as the format string"187 __attribute__((__format__ (__printf__, 2, 3)))188 void log_method(const char *fmt, ...);189 190 void test_format_attribute_method() {191 int n;192 fscanf(stdin, "%d", &n); // Get a tainted value.193 194 // The analyzer used to misinterpret the parameter indices in the format195 // attribute when the format attribute is applied to a method.196 log_method("This number is suspicious: %d\n", n); // no-warning197 }198 199 __attribute__((__format__ (__printf__, 1, 2)))200 static void log_static_method(const char *fmt, ...);201 202 void test_format_attribute_static_method() {203 int n;204 fscanf(stdin, "%d", &n); // Get a tainted value.205 206 log_static_method("This number is suspicious: %d\n", n); // no-warning207 }208};209 210} // namespace format_attribute211