diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index a9ef9aced67e3..50286f2fd32a8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -127,6 +127,11 @@ ANALYZER_OPTION(bool, MayInlineCXXStandardLibrary, "c++-stdlib-inlining", "considered for inlining.", true) +ANALYZER_OPTION(bool, ModelPthreads, "model-pthreads", + "Model Pthreads if enabled - default is disabled ", + false + ) + ANALYZER_OPTION(bool, MayInlineCXXAllocator, "c++-allocator-inlining", "Whether or not allocator and deallocator calls may be " "considered for inlining.", diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 25e28adaee1ad..66f94ff4905a9 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1296,6 +1296,7 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) { void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate, const EvalCallOptions &CallOpts) { + AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); @@ -1322,7 +1323,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // TODO: make this a proper mode // Special case thread creation - if (isThread(*Call)) { + if (isThread(*Call) && Opts.ModelPthreads) { llvm::errs() << "Hijacking pthread_create(3)\n"; threadBifurcate(*Call, D, Bldr, Pred, State); return; diff --git a/clang/test/Analysis/SD-tests/taint-thread.c b/clang/test/Analysis/SD-tests/taint-thread.c new file mode 100644 index 0000000000000..9ff29fa95cd9d --- /dev/null +++ b/clang/test/Analysis/SD-tests/taint-thread.c @@ -0,0 +1,37 @@ +// RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ +// RUN: -analyzer-checker=core,optin.taint.GenericTaint -DPTHREAD_MODEL=1 \ +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true + +// RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ +// RUN: -analyzer-checker=core,optin.taint.GenericTaint -DNO_PTHREAD_MODEL=1 \ +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=false + +#define NULL ((void*) 0) +typedef unsigned long int pthread_t; +typedef struct __pthread_attr pthread_attr_t; +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); + +char *strcat( char *dest, const char *src ); +int scanf(const char*, ...); +int system(const char *command); + +void *thread_func(void *arg) { +#ifdef PTHREAD_MODEL + system( (char *) arg); // expected-warning {{Untrusted data is passed to a system call (CERT/STR02-C. Sanitize data passed to complex subsystems)}} +#endif +#ifdef NO_PTHREAD_MODEL + system( (char *) arg); // expected-no-diagnostics +#endif + return NULL; +} + +// Command Injection Vulnerability Example +void test(void) { + char cmd[2048] = "/bin/cat "; + char filename[1024]; + scanf (" %1023[^\n]", filename); // The attacker can inject a shell escape here + strcat(cmd, filename); + pthread_t p1; + pthread_create(&p1, NULL, &thread_func, &cmd); +} + diff --git a/clang/test/Analysis/SD-tests/thread-modeling-inline.c b/clang/test/Analysis/SD-tests/thread-modeling-inline.c index 566f5f1781d00..a4432ec47f04c 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-inline.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-inline.c @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) enum bool { diff --git a/clang/test/Analysis/SD-tests/thread-modeling-inline2.c b/clang/test/Analysis/SD-tests/thread-modeling-inline2.c index 1241d77ad9bf8..af052b285a854 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-inline2.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-inline2.c @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) enum bool { diff --git a/clang/test/Analysis/SD-tests/thread-modeling-leak.c b/clang/test/Analysis/SD-tests/thread-modeling-leak.c index 5794612c08e7b..f669c85f057d3 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-leak.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-leak.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) diff --git a/clang/test/Analysis/SD-tests/thread-modeling-leak2.c b/clang/test/Analysis/SD-tests/thread-modeling-leak2.c index b903437950cc8..bb89b29414fdc 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-leak2.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-leak2.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) diff --git a/clang/test/Analysis/SD-tests/thread-modeling-null-deref.c b/clang/test/Analysis/SD-tests/thread-modeling-null-deref.c index b04a1816ce571..0e2c9581cc2ca 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-null-deref.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-null-deref.c @@ -1,6 +1,6 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) enum bool { diff --git a/clang/test/Analysis/SD-tests/thread-modeling-null-deref2.c b/clang/test/Analysis/SD-tests/thread-modeling-null-deref2.c index a8a945684b19b..2e5269dc6b07d 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-null-deref2.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-null-deref2.c @@ -1,6 +1,6 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) enum bool { diff --git a/clang/test/Analysis/SD-tests/thread-modeling-path-dependent-leak.c b/clang/test/Analysis/SD-tests/thread-modeling-path-dependent-leak.c index 03c0dc9079df4..8485e2d454252 100644 --- a/clang/test/Analysis/SD-tests/thread-modeling-path-dependent-leak.c +++ b/clang/test/Analysis/SD-tests/thread-modeling-path-dependent-leak.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true #define NULL ((void*) 0) diff --git a/clang/test/Analysis/SD-tests/thread-modeling-ptr-deref.c b/clang/test/Analysis/SD-tests/thread-modeling-ptr-deref.c new file mode 100644 index 0000000000000..68bf750410879 --- /dev/null +++ b/clang/test/Analysis/SD-tests/thread-modeling-ptr-deref.c @@ -0,0 +1,42 @@ +// RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ +// RUN: -analyzer-checker=core -DPTHREAD_MODEL=1 \ +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=true +// +// RUN: %clang_analyze_cc1 -Wno-strict-prototypes -Wno-error=implicit-int -verify %s \ +// RUN: -analyzer-checker=core -DNO_PTHREAD_MODEL=1 \ +// RUN: -analyzer-checker=debug.ExprInspection -analyzer-config model-pthreads=false + +#define NULL ((void*) 0) + +typedef unsigned long int pthread_t; +typedef struct __pthread_attr pthread_attr_t; + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); + +void clang_analyzer_checkInlined(int); +void clang_analyzer_dump(int); + +void* thread_function(void* arg) +{ + // should expect to fail the test at this line if you set the checkInlined to true +#ifdef PTHREAD_MODEL + clang_analyzer_checkInlined(1); // expected-warning{{TRUE}} +#endif + int *ptr = (int *)arg; +#ifdef PTHREAD_MODEL + clang_analyzer_dump(*ptr); // expected-warning{{1 S32b}} +#endif +#ifdef NO_PTHREAD_MODEL + clang_analyzer_dump(*ptr); // expected-warning-re{{reg_${{[[:digit:]]+}}},0 S64b,int}}} +#endif + return NULL; +} + +int test() +{ + int i = 1; + pthread_t p1; + pthread_create(&p1, NULL, &thread_function, &i); + return 0; +} + diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index d5eb790b82f23..24c66e58c59fd 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -99,6 +99,7 @@ // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: model-path = "" +// CHECK-NEXT: model-pthreads = false // CHECK-NEXT: notes-as-events = false // CHECK-NEXT: nullability:NoDiagnoseCallsToSystemHeaders = false // CHECK-NEXT: objc-inlining = true @@ -126,6 +127,7 @@ // CHECK-NEXT: suppress-c++-stdlib = true // CHECK-NEXT: suppress-inlined-defensive-checks = true // CHECK-NEXT: suppress-null-return-paths = true +// CHECK-NEXT: thread-aware = true // CHECK-NEXT: track-conditions = true // CHECK-NEXT: track-conditions-debug = false // CHECK-NEXT: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes = true diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c index c70aeb21ab045..0d82b06a4ddae 100644 --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -7,6 +7,7 @@ // CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List // CHECK-EMPTY: // CHECK-NEXT: apiModeling.Errno +// CHECK-NEXT: apiModeling.Thread // CHECK-NEXT: apiModeling.TrustNonnull // CHECK-NEXT: apiModeling.TrustReturnsNonnull // CHECK-NEXT: apiModeling.llvm.CastValue diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c index faf0a8f19d919..2029cf3672df2 100644 --- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c +++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c @@ -15,6 +15,7 @@ // CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List // CHECK-EMPTY: // CHECK-NEXT: apiModeling.Errno +// CHECK-NEXT: apiModeling.Thread // CHECK-NEXT: apiModeling.TrustNonnull // CHECK-NEXT: apiModeling.TrustReturnsNonnull // CHECK-NEXT: apiModeling.llvm.CastValue