[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:
@@ -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)
|
||||
|
||||
@@ -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">,
|
||||
|
||||
@@ -49,6 +49,7 @@ add_clang_library(clangStaticAnalyzerCheckers
|
||||
IdenticalExprChecker.cpp
|
||||
InnerPointerChecker.cpp
|
||||
InvalidatedIteratorChecker.cpp
|
||||
cert/InvalidPtrChecker.cpp
|
||||
Iterator.cpp
|
||||
IteratorModeling.cpp
|
||||
IteratorRangeChecker.cpp
|
||||
|
||||
279
clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
Normal file
279
clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
Normal 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;
|
||||
}
|
||||
73
clang/test/Analysis/cert/env31-c.c
Normal file
73
clang/test/Analysis/cert/env31-c.c
Normal 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
|
||||
}
|
||||
101
clang/test/Analysis/cert/env34-c-cert-examples.c
Normal file
101
clang/test/Analysis/cert/env34-c-cert-examples.c
Normal 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;
|
||||
}
|
||||
331
clang/test/Analysis/cert/env34-c.c
Normal file
331
clang/test/Analysis/cert/env34-c.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user