diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -2075,10 +2075,63 @@ if (retval < 0 || (size_t)retval >= sizeof(env)) { /* Handle error */ } - + return putenv(env); // putenv function should not be called with auto variables } - + +alpha.security.cert.env +^^^^^^^^^^^^^^^^^^^^^^^ + +SEI CERT checkers of `POSIX C coding rules `_. + +.. _alpha-security-cert-env-InvalidPtr: + +alpha.security.cert.env.InvalidPtr +""""""""""""""""""""""""""" + +Corresponds to SEI CERT Rules ENV31-C and ENV34-C. + +ENV31-C: +Rule is about the possible problem with `main` function's third argument, environment pointer, +"envp". When enviornment array is modified using some modification function +such as putenv, setenv or others, It may happen that memory is reallocated, +however "envp" is not updated to reflect the changes and points to old memory +region. + +ENV34-C: +Some functions return a pointer to a statically allocated buffer. +Consequently, subsequent call of these functions will invalidate previous +pointer. These functions include: getenv, localeconv, asctime, setlocale, strerror + +.. code-block:: c + + int main(int argc, const char *argv[], const char *envp[]) { + if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { + // setenv call may invalidate 'envp' + /* Handle error */ + } + if (envp != NULL) { + for (size_t i = 0; envp[i] != NULL; ++i) { + puts(envp[i]); + // envp may no longer point to the current environment + // this program has unanticipated behavior, since envp + // does not reflect changes made by setenv function. + } + } + return 0; + } + + void previous_call_invalidation() { + char *p, *pp; + + p = getenv("VAR"); + pp = getenv("VAR2"); + // subsequent call to 'getenv' invalidated previous one + + *p; + // dereferencing invalid pointer + } + .. _alpha-security-ArrayBound: alpha.security.ArrayBound (C) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -73,6 +73,7 @@ def CERT : Package<"cert">, ParentPackage; def POS : Package<"pos">, ParentPackage; +def ENV : Package<"env">, ParentPackage; def Unix : Package<"unix">; def UnixAlpha : Package<"unix">, ParentPackage; @@ -947,6 +948,14 @@ } // end "alpha.cert.pos" +let ParentPackage = ENV in { + + def InvalidPtrChecker : Checker<"InvalidPtr">, + HelpText<"Finds usages of possibly invalidated pointers">, + Documentation; + +} // end "alpha.cert.env" + let ParentPackage = SecurityAlpha in { def ArrayBoundChecker : Checker<"ArrayBound">, diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -49,6 +49,7 @@ IdenticalExprChecker.cpp InnerPointerChecker.cpp InvalidatedIteratorChecker.cpp + cert/InvalidPtrChecker.cpp Iterator.cpp IteratorModeling.cpp IteratorRangeChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp @@ -0,0 +1,279 @@ +//== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines InvalidPtrChecker which finds usages of possibly +// invalidated pointer. +// CERT SEI Rules ENV31-C and ENV34-C +// For more information see: +// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ +// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class InvalidPtrChecker + : public Checker { +private: + BugType BT{this, "Use of invalidated pointer", categories::MemoryError}; + + void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const; + + using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call, + CheckerContext &C) const; + + // SEI CERT ENV31-C + const CallDescriptionMap EnvpInvalidatingFunctions = { + {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + }; + + void postPreviousReturnInvalidatingCall(const CallEvent &Call, + CheckerContext &C) const; + + // SEI CERT ENV34-C + const CallDescriptionMap PreviousCallInvalidatingFunctions = { + {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{"setlocale", 2}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{"localeconv", 0}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + }; + +public: + // Obtain the environment pointer from 'main()' (if present). + void checkBeginFunction(CheckerContext &C) const; + + // Handle functions in EnvpInvalidatingFunctions, that invalidate environment + // pointer from 'main()' + // Handle functions in PreviousCallInvalidatingFunctions. + // Also, check if invalidated region is passed to a + // conservatively evaluated function call as an argument. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + // Check if invalidated region is being dereferenced. + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; +}; + +} // namespace + +// Set of memory regions that were invalidated +REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) + +// Stores the region of the environment pointer of 'main' (if present). +// Note: This pointer has type 'const MemRegion *', however the trait is only +// specialized to 'const void*' and 'void*' +REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *) + +// Stores key-value pairs, where key is function declaration and value is +// pointer to memory region returned by previous call of this function +REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, + const MemRegion *) + +void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, + CheckerContext &C) const { + StringRef FunctionName = Call.getCalleeIdentifier()->getName(); + ProgramStateRef State = C.getState(); + const auto *Reg = State->get(); + if (!Reg) + return; + const auto *SymbolicEnvPtrRegion = + reinterpret_cast(const_cast(Reg)); + + State = State->add(SymbolicEnvPtrRegion); + + const NoteTag *Note = + C.getNoteTag([SymbolicEnvPtrRegion, FunctionName]( + PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { + if (!BR.isInteresting(SymbolicEnvPtrRegion)) + return; + Out << '\'' << FunctionName + << "' call may invalidate the environment parameter of 'main'"; + }); + + C.addTransition(State, Note); +} + +void InvalidPtrChecker::postPreviousReturnInvalidatingCall( + const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + const NoteTag *Note = nullptr; + const FunctionDecl *FD = dyn_cast_or_null(Call.getDecl()); + // Invalidate the region of the previously returned pointer - if there was + // one. + if (const MemRegion *const *Reg = State->get(FD)) { + const MemRegion *PrevReg = *Reg; + State = State->add(PrevReg); + Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR, + llvm::raw_ostream &Out) { + if (!BR.isInteresting(PrevReg)) + return; + Out << '\''; + FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); + Out << "' call may invalidate the the result of the previous " << '\''; + FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); + Out << '\''; + }); + } + + const LocationContext *LCtx = C.getLocationContext(); + const auto *CE = cast(Call.getOriginExpr()); + + // Function call will return a pointer to the new symbolic region. + DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( + CE, LCtx, CE->getType(), C.blockCount()); + State = State->BindExpr(CE, LCtx, RetVal); + + // Remember to this region. + const auto *SymRegOfRetVal = dyn_cast(RetVal.getAsRegion()); + const MemRegion *MR = + const_cast(SymRegOfRetVal->getBaseRegion()); + State = State->set(FD, MR); + + ExplodedNode *Node = C.addTransition(State, Note); + const NoteTag *PreviousCallNote = + C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { + if (!BR.isInteresting(MR)) + return; + Out << '\'' << "'previous function call was here" << '\''; + }); + + C.addTransition(State, Node, PreviousCallNote); +} + +// TODO: This seems really ugly. Simplify this. +static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, + const MemRegion *Reg) { + while (Reg) { + if (State->contains(Reg)) + return Reg; + const auto *SymBase = Reg->getSymbolicBase(); + if (!SymBase) + break; + const auto *SRV = dyn_cast(SymBase->getSymbol()); + if (!SRV) + break; + Reg = SRV->getRegion(); + if (const auto *VarReg = dyn_cast(SRV->getRegion())) + Reg = VarReg; + } + return nullptr; +} + +// Handle functions in EnvpInvalidatingFunctions, that invalidate environment +// pointer from 'main()' Also, check if invalidated region is passed to a +// function call as an argument. +void InvalidPtrChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // Check if function invalidates 'envp' argument of 'main' + if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) + (this->**Handler)(Call, C); + + // Check if function invalidates the result of previous call + if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) + (this->**Handler)(Call, C); + + // Check if one of the arguments of the function call is invalidated + + // If call was inlined, don't report invalidated argument + if (C.wasInlined) + return; + + ProgramStateRef State = C.getState(); + + for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { + + if (const auto *SR = dyn_cast_or_null( + Call.getArgSVal(I).getAsRegion())) { + if (const MemRegion *InvalidatedSymbolicBase = + findInvalidatedSymbolicBase(State, SR)) { + ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); + if (!ErrorNode) + return; + + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << "use of invalidated pointer '"; + Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, + C.getASTContext().getPrintingPolicy()); + Out << "' in a function call"; + + auto Report = + std::make_unique(BT, Out.str(), ErrorNode); + Report->markInteresting(InvalidatedSymbolicBase); + Report->addRange(Call.getArgSourceRange(I)); + C.emitReport(std::move(Report)); + } + } + } +} + +// Obtain the environment pointer from 'main()', if present. +void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + + const auto *FD = dyn_cast(C.getLocationContext()->getDecl()); + if (!FD || FD->param_size() != 3 || !FD->isMain()) + return; + + ProgramStateRef State = C.getState(); + const MemRegion *EnvpReg = + State->getRegion(FD->parameters()[2], C.getLocationContext()); + + // Save the memory region pointed by the environment pointer parameter of + // 'main'. + State = State->set( + reinterpret_cast(const_cast(EnvpReg))); + C.addTransition(State); +} + +// Check if invalidated region is being dereferenced. +void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Ignore memory operations involving 'non-invalidated' locations. + const MemRegion *InvalidatedSymbolicBase = + findInvalidatedSymbolicBase(State, Loc.getAsRegion()); + if (!InvalidatedSymbolicBase) + return; + + ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); + if (!ErrorNode) + return; + + auto Report = std::make_unique( + BT, "dereferencing an invalid pointer", ErrorNode); + Report->markInteresting(InvalidatedSymbolicBase); + C.emitReport(std::move(Report)); +} + +void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { + return true; +} diff --git a/clang/test/Analysis/cert/env31-c.c b/clang/test/Analysis/cert/env31-c.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cert/env31-c.c @@ -0,0 +1,73 @@ +// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify=putenv,common \ +// RUN: -DENV_INVALIDATING_CALL="putenv(\"X=Y\")" +// +// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify=putenvs,common \ +// RUN: -DENV_INVALIDATING_CALL="_putenv_s(\"X\", \"Y\")" +// +// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify=wputenvs,common \ +// RUN: -DENV_INVALIDATING_CALL="_wputenv_s(\"X\", \"Y\")" +// +// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify=setenv,common \ +// RUN: -DENV_INVALIDATING_CALL="setenv(\"X\", \"Y\", 0)" +// +// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify=unsetenv,common \ +// RUN: -DENV_INVALIDATING_CALL="unsetenv(\"X\")" + +typedef int errno_t; +typedef char wchar_t; + +int putenv(char *string); +errno_t _putenv_s(const char *varname, const char *value_string); +errno_t _wputenv_s(const wchar_t *varname, const wchar_t *value_string); +int setenv(const char *name, const char *value, int overwrite); +int unsetenv(const char *name); + +void fn_without_body(char **e); +void fn_with_body(char **e) {} + +void call_env_invalidating_fn(char **e) { + ENV_INVALIDATING_CALL; + // putenv-note@-1 5 {{'putenv' call may invalidate the environment parameter of 'main'}} + // putenvs-note@-2 5 {{'_putenv_s' call may invalidate the environment parameter of 'main'}} + // wputenvs-note@-3 5 {{'_wputenv_s' call may invalidate the environment parameter of 'main'}} + // setenv-note@-4 5 {{'setenv' call may invalidate the environment parameter of 'main'}} + // unsetenv-note@-5 5 {{'unsetenv' call may invalidate the environment parameter of 'main'}} + + *e; + // common-warning@-1 {{dereferencing an invalid pointer}} + // common-note@-2 {{dereferencing an invalid pointer}} +} + +int main(int argc, char *argv[], char *envp[]) { + char **e = envp; + *e; // no-warning + e[0]; // no-warning + *envp; // no-warning + call_env_invalidating_fn(e); + // common-note@-1 5 {{Calling 'call_env_invalidating_fn'}} + // common-note@-2 4 {{Returning from 'call_env_invalidating_fn'}} + + *e; + // common-warning@-1 {{dereferencing an invalid pointer}} + // common-note@-2 {{dereferencing an invalid pointer}} + + *envp; + // common-warning@-1 2 {{dereferencing an invalid pointer}} + // common-note@-2 2 {{dereferencing an invalid pointer}} + + fn_without_body(e); + // common-warning@-1 {{use of invalidated pointer 'e' in a function call}} + // common-note@-2 {{use of invalidated pointer 'e' in a function call}} + + fn_with_body(e); // no-warning +} diff --git a/clang/test/Analysis/cert/env34-c-cert-examples.c b/clang/test/Analysis/cert/env34-c-cert-examples.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cert/env34-c-cert-examples.c @@ -0,0 +1,101 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \ +// RUN: -verify -Wno-unused %s + +#include "../Inputs/system-header-simulator.h" +char *getenv(const char *name); +int strcmp(const char*, const char*); +char *strdup(const char*); +void free(void *memblock); +void *malloc(size_t size); + +void incorrect_usage(void) { + char *tmpvar; + char *tempvar; + + tmpvar = getenv("TMP"); + + if (!tmpvar) + return; + + tempvar = getenv("TEMP"); + + if (!tempvar) + return; + + if (strcmp(tmpvar, tempvar) == 0) { // body of strcmp is unknown + // expected-warning@-1{{use of invalidated pointer 'tmpvar' in a function call}} + } +} + +void correct_usage_1(void) { + char *tmpvar; + char *tempvar; + + const char *temp = getenv("TMP"); + if (temp != NULL) { + tmpvar = (char *)malloc(strlen(temp)+1); + if (tmpvar != NULL) { + strcpy(tmpvar, temp); + } else { + return; + } + } else { + return; + } + + temp = getenv("TEMP"); + if (temp != NULL) { + tempvar = (char *)malloc(strlen(temp)+1); + if (tempvar != NULL) { + strcpy(tempvar, temp); + } else { + return; + } + } else { + return; + } + + if (strcmp(tmpvar, tempvar) == 0) { + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } + free(tmpvar); + free(tempvar); +} + +void correct_usage_2(void) { + char *tmpvar; + char *tempvar; + + const char *temp = getenv("TMP"); + if (temp != NULL) { + tmpvar = strdup(temp); + if (tmpvar == NULL) { + return; + } + } else { + return; + } + + temp = getenv("TEMP"); + if (temp != NULL) { + tempvar = strdup(temp); + if (tempvar == NULL) { + return; + } + } else { + return; + } + + if (strcmp(tmpvar, tempvar) == 0) { + printf("TMP and TEMP are the same.\n"); + } else { + printf("TMP and TEMP are NOT the same.\n"); + } + free(tmpvar); + tmpvar = NULL; + free(tempvar); + tempvar = NULL; +} diff --git a/clang/test/Analysis/cert/env34-c.c b/clang/test/Analysis/cert/env34-c.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/cert/env34-c.c @@ -0,0 +1,331 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=alpha.security.cert.env.InvalidPtr\ +// RUN: -analyzer-output=text -verify -Wno-unused %s + +#include "../Inputs/system-header-simulator.h" +char *getenv(const char *name); +char *setlocale(int category, const char *locale); +char *strerror(int errnum); + +typedef struct { + char * field; +} lconv; +lconv *localeconv(void); + +typedef struct { +} tm; +char *asctime(const tm *timeptr); + +int strcmp(const char*, const char*); +extern void foo(char *e); +extern char* bar(); + + +void getenv_test1() { + char *p; + + p = getenv("VAR"); + *p; // no-warning + + p = getenv("VAR2"); + *p; // no-warning, getenv result was assigned to the same pointer +} + +void getenv_test2() { + char *p, *p2; + + p = getenv("VAR"); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + p2 = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test3() { + char *p, *p2, *p3; + + p = getenv("VAR"); + *p; // no-warning + + p = getenv("VAR2"); + // expected-note@-1{{previous function call was here}} + p2 = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + p3 = getenv("VAR3"); + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test4() { + char *p, *p2, *p3; + + p = getenv("VAR"); + // expected-note@-1{{previous function call was here}} + p2 = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + p3 = getenv("VAR3"); + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test5() { + char *p, *p2, *p3; + + p = getenv("VAR"); + p2 = getenv("VAR2"); + // expected-note@-1{{previous function call was here}} + p3 = getenv("VAR3"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + *p2; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test6() { + char *p, *p2; + p = getenv("VAR"); + *p; // no-warning + + p = getenv("VAR2"); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + p2 = getenv("VAR3"); + // expected-note@-1{{previous function call was here}} + // expected-note@-2{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} + + *p2; // no-warning + + p = getenv("VAR4"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + *p; // no-warning + *p2; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test7() { + char *p, *p2; + p = getenv("VAR"); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + p2 = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + foo(p); + // expected-warning@-1{{use of invalidated pointer 'p' in a function call}} + // expected-note@-2{{use of invalidated pointer 'p' in a function call}} +} + +void getenv_test8() { + static const char *array[] = { + 0, + 0, + "/var/tmp", + "/usr/tmp", + "/tmp", + "." + }; + + if( !array[0] ) + // expected-note@-1{{Taking true branch}} + array[0] = getenv("TEMPDIR"); + // expected-note@-1{{previous function call was here}} + + if( !array[1] ) + // expected-note@-1{{Taking true branch}} + array[1] = getenv("TMPDIR"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + *array[0]; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test9() { + char *p, *p2; + p = getenv("something"); + p = bar(); + p2 = getenv("something"); + *p; // no-warning: p does not point to getenv anymore +} + +void getenv_test10() { + strcmp(getenv("VAR1"), getenv("VAR2")); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + // expected-note@-2{{previous function call was here}} + // expected-warning@-3{{use of invalidated pointer 'getenv("VAR1")' in a function call}} + // expected-note@-4{{use of invalidated pointer 'getenv("VAR1")' in a function call}} +} + +void dereference_pointer(char* a) { + *a; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void getenv_test11() { + char *p = getenv("VAR"); + // expected-note@-1{{previous function call was here}} + + char *pp = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + + dereference_pointer(p); + // expected-note@-1{{Calling 'dereference_pointer'}} +} + +void getenv_test12(int flag1, int flag2) { + char *p = getenv("VAR"); + // expected-note@-1{{previous function call was here}} + + if (flag1) { + // expected-note@-1{{Assuming 'flag1' is not equal to 0}} + // expected-note@-2{{Taking true branch}} + char *pp = getenv("VAR2"); + // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}} + } + + if (flag2) { + // expected-note@-1{{Assuming 'flag2' is not equal to 0}} + // expected-note@-2{{Taking true branch}} + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} + } +} + +void setlocale_test1() { + char *p, *p2; + p = setlocale(0, "VAR"); + *p; // no-warning + + p = setlocale(0, "VAR2"); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + p2 = setlocale(0, "VAR3"); + // expected-note@-1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}} + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void setlocale_test2(int flag) { + char *p, *p2; + p = setlocale(0, "VAR"); + *p; // no-warning + + p = setlocale(0, "VAR2"); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + if (flag) { + // expected-note@-1{{Assuming 'flag' is not equal to 0}} + // expected-note@-2{{Taking true branch}} + p2 = setlocale(0, "VAR3"); + // expected-note@-1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}} + } + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void strerror_test1() { + char *p, *p2; + + p = strerror(0); + *p; // no-warning + + p = strerror(1); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + p2 = strerror(2); + // expected-note@-1{{'strerror' call may invalidate the the result of the previous 'strerror'}} + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void strerror_test2(int errno) { + char *p, *p2; + + p = strerror(0); + *p; // no-warning + + p = strerror(1); + // expected-note@-1{{previous function call was here}} + *p; // no-warning + + if (0 == 1) { + // expected-note@-1{{0 is not equal to 1}} + // expected-note@-2{{Taking false branch}} + p2 = strerror(2); + } + + *p; // no-warning + + if (errno) { + // expected-note@-1{{Assuming 'errno' is not equal to 0}} + // expected-note@-2{{Taking true branch}} + p2 = strerror(errno); + // expected-note@-1{{'strerror' call may invalidate the the result of the previous 'strerror'}} + } + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void asctime_test() { + const tm *t; + const tm *tt; + + char* p = asctime(t); + // expected-note@-1{{previous function call was here}} + char* pp = asctime(tt); + // expected-note@-1{{'asctime' call may invalidate the the result of the previous 'asctime'}} + + *p; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void localeconv_test1() { + lconv *lc1 = localeconv(); + // expected-note@-1{{previous function call was here}} + lconv *lc2 = localeconv(); + // expected-note@-1{{'localeconv' call may invalidate the the result of the previous 'localeconv'}} + + *lc1; + // expected-warning@-1{{dereferencing an invalid pointer}} + // expected-note@-2{{dereferencing an invalid pointer}} +} + +void localeconv_test2() { + // TODO: false negative + lconv *lc1 = localeconv(); + lconv *lc2 = localeconv(); + lc1->field; +}