Skip to content

Commit 3c23047

Browse files
authored
[clang][analyzer] Move checker 'cert.pos.34c' (in alpha.security) into 'PutenvStackArray' (#92424)
The "cert" package looks not useful and the checker has not a meaningful name with the old naming scheme. Additionally tests and documentation is updated.
1 parent 6552af1 commit 3c23047

File tree

7 files changed

+119
-187
lines changed

7 files changed

+119
-187
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,6 +2833,31 @@ Warn on mmap() calls that are both writable and executable.
28332833
// code
28342834
}
28352835
2836+
.. _alpha-security-putenv-stack-array:
2837+
2838+
alpha.security.PutenvStackArray (C)
2839+
"""""""""""""""""""""""""""""""""""
2840+
Finds calls to the ``putenv`` function which pass a pointer to a stack-allocated
2841+
(automatic) array as the argument. Function ``putenv`` does not copy the passed
2842+
string, only a pointer to the data is stored and this data can be read even by
2843+
other threads. Content of a stack-allocated array is likely to be overwritten
2844+
after returning from the parent function.
2845+
2846+
The problem can be solved by using a static array variable or dynamically
2847+
allocated memory. Even better is to avoid using ``putenv`` (it has other
2848+
problems related to memory leaks) and use ``setenv`` instead.
2849+
2850+
The check corresponds to CERT rule
2851+
`POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument
2852+
<https://wiki.sei.cmu.edu/confluence/display/c/POS34-C.+Do+not+call+putenv%28%29+with+a+pointer+to+an+automatic+variable+as+the+argument>`_.
2853+
2854+
.. code-block:: c
2855+
2856+
int f() {
2857+
char env[] = "NAME=value";
2858+
return putenv(env); // putenv function should not be called with stack-allocated string
2859+
}
2860+
28362861
.. _alpha-security-ReturnPtrRange:
28372862
28382863
alpha.security.ReturnPtrRange (C)
@@ -2859,55 +2884,6 @@ alpha.security.cert
28592884
28602885
SEI CERT checkers which tries to find errors based on their `C coding rules <https://wiki.sei.cmu.edu/confluence/display/c/2+Rules>`_.
28612886
2862-
.. _alpha-security-cert-pos-checkers:
2863-
2864-
alpha.security.cert.pos
2865-
^^^^^^^^^^^^^^^^^^^^^^^
2866-
2867-
SEI CERT checkers of `POSIX C coding rules <https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152405>`_.
2868-
2869-
.. _alpha-security-cert-pos-34c:
2870-
2871-
alpha.security.cert.pos.34c
2872-
"""""""""""""""""""""""""""
2873-
Finds calls to the ``putenv`` function which pass a pointer to an automatic variable as the argument.
2874-
2875-
.. code-block:: c
2876-
2877-
int func(const char *var) {
2878-
char env[1024];
2879-
int retval = snprintf(env, sizeof(env),"TEST=%s", var);
2880-
if (retval < 0 || (size_t)retval >= sizeof(env)) {
2881-
/* Handle error */
2882-
}
2883-
2884-
return putenv(env); // putenv function should not be called with auto variables
2885-
}
2886-
2887-
Limitations:
2888-
2889-
- Technically, one can pass automatic variables to ``putenv``,
2890-
but one needs to ensure that the given environment key stays
2891-
alive until it's removed or overwritten.
2892-
Since the analyzer cannot keep track of which envvars get overwritten
2893-
and when, it needs to be slightly more aggressive and warn for such
2894-
cases too, leading in some cases to false-positive reports like this:
2895-
2896-
.. code-block:: c
2897-
2898-
void baz() {
2899-
char env[] = "NAME=value";
2900-
putenv(env); // false-positive warning: putenv function should not be called...
2901-
// More code...
2902-
putenv((char *)"NAME=anothervalue");
2903-
// This putenv call overwrites the previous entry, thus that can no longer dangle.
2904-
} // 'env' array becomes dead only here.
2905-
2906-
alpha.security.cert.env
2907-
^^^^^^^^^^^^^^^^^^^^^^^
2908-
2909-
SEI CERT checkers of `Environment C coding rules <https://wiki.sei.cmu.edu/confluence/x/JdcxBQ>`_.
2910-
29112887
alpha.security.taint
29122888
^^^^^^^^^^^^^^^^^^^^
29132889

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,15 +1035,6 @@ let ParentPackage = ENV in {
10351035

10361036
} // end "security.cert.env"
10371037

1038-
let ParentPackage = POSAlpha in {
1039-
1040-
def PutenvWithAuto : Checker<"34c">,
1041-
HelpText<"Finds calls to the 'putenv' function which pass a pointer to "
1042-
"an automatic variable as the argument.">,
1043-
Documentation<HasDocumentation>;
1044-
1045-
} // end "alpha.cert.pos"
1046-
10471038
let ParentPackage = SecurityAlpha in {
10481039

10491040
def ArrayBoundChecker : Checker<"ArrayBound">,
@@ -1054,10 +1045,6 @@ def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">,
10541045
HelpText<"Warn about buffer overflows (newer checker)">,
10551046
Documentation<HasDocumentation>;
10561047

1057-
def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,
1058-
HelpText<"Check for an out-of-bound pointer being returned to callers">,
1059-
Documentation<HasDocumentation>;
1060-
10611048
def MallocOverflowSecurityChecker : Checker<"MallocOverflow">,
10621049
HelpText<"Check for overflows in the arguments to malloc()">,
10631050
Documentation<HasDocumentation>;
@@ -1078,6 +1065,15 @@ def MmapWriteExecChecker : Checker<"MmapWriteExec">,
10781065
]>,
10791066
Documentation<HasDocumentation>;
10801067

1068+
def PutenvStackArray : Checker<"PutenvStackArray">,
1069+
HelpText<"Finds calls to the function 'putenv' which pass a pointer to "
1070+
"an automatic (stack-allocated) array as the argument.">,
1071+
Documentation<HasDocumentation>;
1072+
1073+
def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,
1074+
HelpText<"Check for an out-of-bound pointer being returned to callers">,
1075+
Documentation<HasDocumentation>;
1076+
10811077
} // end "alpha.security"
10821078

10831079
//===----------------------------------------------------------------------===//

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ add_clang_library(clangStaticAnalyzerCheckers
9696
PointerSortingChecker.cpp
9797
PointerSubChecker.cpp
9898
PthreadLockChecker.cpp
99-
cert/PutenvWithAutoChecker.cpp
99+
PutenvStackArrayChecker.cpp
100100
RetainCountChecker/RetainCountChecker.cpp
101101
RetainCountChecker/RetainCountDiagnostics.cpp
102102
ReturnPointerRangeChecker.cpp

clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp renamed to clang/lib/StaticAnalyzer/Checkers/PutenvStackArrayChecker.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
//== PutenvWithAutoChecker.cpp --------------------------------- -*- C++ -*--=//
1+
//== PutenvStackArrayChecker.cpp ------------------------------- -*- C++ -*--=//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// This file defines PutenvWithAutoChecker which finds calls of ``putenv``
10-
// function with automatic variable as the argument.
9+
// This file defines PutenvStackArrayChecker which finds calls of ``putenv``
10+
// function with automatic array variable as the argument.
1111
// https://wiki.sei.cmu.edu/confluence/x/6NYxBQ
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
#include "../AllocationState.h"
15+
#include "AllocationState.h"
1616
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1717
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1818
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -26,9 +26,9 @@ using namespace clang;
2626
using namespace ento;
2727

2828
namespace {
29-
class PutenvWithAutoChecker : public Checker<check::PostCall> {
29+
class PutenvStackArrayChecker : public Checker<check::PostCall> {
3030
private:
31-
BugType BT{this, "'putenv' function should not be called with auto variables",
31+
BugType BT{this, "'putenv' called with stack-allocated string",
3232
categories::SecurityError};
3333
const CallDescription Putenv{CDM::CLibrary, {"putenv"}, 1};
3434

@@ -37,8 +37,8 @@ class PutenvWithAutoChecker : public Checker<check::PostCall> {
3737
};
3838
} // namespace
3939

40-
void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
41-
CheckerContext &C) const {
40+
void PutenvStackArrayChecker::checkPostCall(const CallEvent &Call,
41+
CheckerContext &C) const {
4242
if (!Putenv.matches(Call))
4343
return;
4444

@@ -50,7 +50,7 @@ void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
5050
return;
5151

5252
StringRef ErrorMsg = "The 'putenv' function should not be called with "
53-
"arguments that have automatic storage";
53+
"arrays that have automatic storage";
5454
ExplodedNode *N = C.generateErrorNode();
5555
auto Report = std::make_unique<PathSensitiveBugReport>(BT, ErrorMsg, N);
5656

@@ -60,8 +60,10 @@ void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
6060
C.emitReport(std::move(Report));
6161
}
6262

63-
void ento::registerPutenvWithAuto(CheckerManager &Mgr) {
64-
Mgr.registerChecker<PutenvWithAutoChecker>();
63+
void ento::registerPutenvStackArray(CheckerManager &Mgr) {
64+
Mgr.registerChecker<PutenvStackArrayChecker>();
6565
}
6666

67-
bool ento::shouldRegisterPutenvWithAuto(const CheckerManager &) { return true; }
67+
bool ento::shouldRegisterPutenvStackArray(const CheckerManager &) {
68+
return true;
69+
}

clang/test/Analysis/cert/pos34-c-fp-suppression.cpp

Lines changed: 0 additions & 51 deletions
This file was deleted.

clang/test/Analysis/cert/pos34-c.cpp

Lines changed: 0 additions & 61 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %clang_analyze_cc1 \
2+
// RUN: -analyzer-checker=alpha.security.PutenvStackArray \
3+
// RUN: -verify %s
4+
5+
#include "Inputs/system-header-simulator.h"
6+
void free(void *);
7+
void *malloc(size_t);
8+
int putenv(char *);
9+
int snprintf(char *, size_t, const char *, ...);
10+
11+
int test_auto_var(const char *var) {
12+
char env[1024];
13+
(void)snprintf(env, sizeof(env), "TEST=%s", var);
14+
return putenv(env); // expected-warning{{The 'putenv' function should not be called with arrays that have automatic storage}}
15+
}
16+
17+
int test_static_var(const char *var) {
18+
static char env[1024];
19+
(void)snprintf(env, sizeof(env), "TEST=%s", var);
20+
return putenv(env); // no-warning: static array is used
21+
}
22+
23+
void test_heap_memory(const char *var) {
24+
const char *env_format = "TEST=%s";
25+
const size_t len = strlen(var) + strlen(env_format);
26+
char *env = (char *)malloc(len);
27+
if (env == NULL)
28+
return;
29+
if (putenv(env) != 0) // no-warning: env was dynamically allocated.
30+
free(env);
31+
}
32+
33+
typedef struct {
34+
int A;
35+
char Env[1024];
36+
} Mem;
37+
38+
int test_auto_var_struct() {
39+
Mem mem;
40+
return putenv(mem.Env); // expected-warning{{The 'putenv' function should not be called with}}
41+
}
42+
43+
int test_auto_var_subarray() {
44+
char env[1024];
45+
return putenv(env + 100); // expected-warning{{The 'putenv' function should not be called with}}
46+
}
47+
48+
int test_constant() {
49+
char *env = "TEST";
50+
return putenv(env); // no-warning: data is not on the stack
51+
}
52+
53+
extern char *ext_env;
54+
int test_extern() {
55+
return putenv(ext_env); // no-warning: extern storage class.
56+
}
57+
58+
void test_auto_var_reset() {
59+
char env[] = "NAME=value";
60+
putenv(env); // expected-warning{{The 'putenv' function should not be called with}}
61+
// ... (do something)
62+
// Even cases like this are likely a bug:
63+
// It looks like that if one string was passed to putenv,
64+
// it should not be deallocated at all, because when reading the
65+
// environment variable a pointer into this string is returned.
66+
// In this case, if another (or the same) thread reads variable "NAME"
67+
// at this point and does not copy the returned string, the data may
68+
// become invalid.
69+
putenv((char *)"NAME=anothervalue");
70+
}

0 commit comments

Comments
 (0)