[analyzer] Add InvalidPtrChecker

This patch introduces a new checker: `alpha.security.cert.env.InvalidPtr`

Checker finds usage of invalidated pointers related to environment.

Based on the following SEI CERT Rules:
ENV34-C: https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
ENV31-C: https://wiki.sei.cmu.edu/confluence/x/5NUxBQ

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D97699
This commit is contained in:
Zurab Tsinadze
2021-09-18 22:54:59 +02:00
parent c63a9a79af
commit 811b1736d9
7 changed files with 849 additions and 2 deletions

View File

@@ -2075,10 +2075,63 @@ Finds calls to the ``putenv`` function which pass a pointer to an automatic vari
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 <https://wiki.sei.cmu.edu/confluence/x/JdcxBQ>`_.
.. _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)

View File

@@ -73,6 +73,7 @@ def Taint : Package<"taint">, ParentPackage<SecurityAlpha>;
def CERT : Package<"cert">, ParentPackage<SecurityAlpha>;
def POS : Package<"pos">, ParentPackage<CERT>;
def ENV : Package<"env">, ParentPackage<CERT>;
def Unix : Package<"unix">;
def UnixAlpha : Package<"unix">, ParentPackage<Alpha>;
@@ -947,6 +948,14 @@ let ParentPackage = POS in {
} // end "alpha.cert.pos"
let ParentPackage = ENV in {
def InvalidPtrChecker : Checker<"InvalidPtr">,
HelpText<"Finds usages of possibly invalidated pointers">,
Documentation<HasDocumentation>;
} // end "alpha.cert.env"
let ParentPackage = SecurityAlpha in {
def ArrayBoundChecker : Checker<"ArrayBound">,

View File

@@ -49,6 +49,7 @@ add_clang_library(clangStaticAnalyzerCheckers
IdenticalExprChecker.cpp
InnerPointerChecker.cpp
InvalidatedIteratorChecker.cpp
cert/InvalidPtrChecker.cpp
Iterator.cpp
IteratorModeling.cpp
IteratorRangeChecker.cpp

View File

@@ -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<check::Location, check::BeginFunction, check::PostCall> {
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<HandlerFn> 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<HandlerFn> 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<EnvPtrRegion>();
if (!Reg)
return;
const auto *SymbolicEnvPtrRegion =
reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
State = State->add<InvalidMemoryRegions>(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<FunctionDecl>(Call.getDecl());
// Invalidate the region of the previously returned pointer - if there was
// one.
if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
const MemRegion *PrevReg = *Reg;
State = State->add<InvalidMemoryRegions>(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<CallExpr>(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<SymbolicRegion>(RetVal.getAsRegion());
const MemRegion *MR =
const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
State = State->set<PreviousCallResultMap>(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<InvalidMemoryRegions>(Reg))
return Reg;
const auto *SymBase = Reg->getSymbolicBase();
if (!SymBase)
break;
const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
if (!SRV)
break;
Reg = SRV->getRegion();
if (const auto *VarReg = dyn_cast<VarRegion>(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<SymbolicRegion>(
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<PathSensitiveBugReport>(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<FunctionDecl>(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<EnvPtrRegion>(
reinterpret_cast<void *>(const_cast<MemRegion *>(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<PathSensitiveBugReport>(
BT, "dereferencing an invalid pointer", ErrorNode);
Report->markInteresting(InvalidatedSymbolicBase);
C.emitReport(std::move(Report));
}
void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
Mgr.registerChecker<InvalidPtrChecker>();
}
bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
return true;
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;
}