CertC-ENV31¶
Do not rely on an environment pointer following an operation that may invalidate it
Required inputs: IR, StaticSemanticAnalysis
Some implementations provide a nonportable environment pointer that is valid
when
main() is called but may be invalidated by operations that modify
the environment.
The C Standard, J.5.1 [ ISO/IEC 9899:2011], states
In a hosted environment, the main function receives a third argument,
char *envp[], that points to a null-terminated array of pointers tochar, each of which points to a string that provides information about the environment for this execution of the program.
Consequently, under a
hosted
environment supporting this common extension, it is possible to access the
environment through a modified form of
main():
main(int argc, char *argv[], char *envp[]){ /* ... */ }
However, modifying the environment by any means may cause the environment
memory to be reallocated, with the result that
envp now references an incorrect location. For example, when
compiled with GCC 4.8.1 and run on a 32-bit Intel GNU/Linux machine, the
following code,
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main(int argc, const char *argv[], const char *envp[]) {
printf("environ: %p\n", environ);
printf("envp: %p\n", envp);
setenv("MY_NEW_VAR", "new_value", 1);
puts("--Added MY_NEW_VAR--");
printf("environ: %p\n", environ);
printf("envp: %p\n", envp);
return 0;
}
yields
% ./envp-environ environ: 0xbf8656ec envp: 0xbf8656ec --Added MY_NEW_VAR-- environ: 0x804a008 envp: 0xbf8656ec
It is evident from these results that the environment has been relocated as a
result of the call to
setenv(). The external variable
environ is updated to refer to the current environment; the
envp parameter is not.
An environment pointer may also become invalidated by subsequent calls to
getenv(). (See
ENV34-C.
Do not store pointers returned by certain functions for more
information.)
Noncompliant Code Example (POSIX)
After a call to the POSIX
setenv() function or to another function that modifies the
environment, the
envp pointer may no longer reference the current environment. The
Portable Operating System Interface (POSIX®), Base
Specifications, Issue 7 [
IEEE
Std 1003.1:2013], states
Unanticipated results may occur if
setenv()changes the external variableenviron. In particular, if the optionalenvpargument tomain()is present, it is not changed, and thus may point to an obsolete copy of the environment (as may any other copy ofenviron).
This noncompliant code example accesses the
envp pointer after calling
setenv():
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[], const char *envp[]) {
if (setenv("MY_NEW_VAR", "new_value", 1) != 0) {
/* Handle error */
}
if (envp != NULL) {
for (size_t i = 0; envp[i] != NULL; ++i) {
puts(envp[i]);
}
}
return 0;
}
Because
envp may no longer point to the current environment, this program
has unanticipated behavior.
Compliant Solution (POSIX)
Use
environ in place of
envp when defined:
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main(void) {
if (setenv("MY_NEW_VAR", "new_value", 1) != 0) {
/* Handle error */
}
if (environ != NULL) {
for (size_t i = 0; environ[i] != NULL; ++i) {
puts(environ[i]);
}
}
return 0;
}
Noncompliant Code Example (Windows)
After a call to the Windows
_putenv_s() function or to another function that modifies the
environment, the
envp pointer may no longer reference the environment.
According to the Visual C++ reference [ MSDN]
The environment block passed to
mainandwmainis a "frozen" copy of the current environment. If you subsequently change the environment via a call to_putenvor_wputenv, the current environment (as returned bygetenv/_wgetenvand the_environ/_wenvironvariable) will change, but the block pointed to byenvpwill not change.
This noncompliant code example accesses the
envp pointer after calling
_putenv_s():
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[], const char *envp[]) {
if (_putenv_s("MY_NEW_VAR", "new_value") != 0) {
/* Handle error */
}
if (envp != NULL) {
for (size_t i = 0; envp[i] != NULL; ++i) {
puts(envp[i]);
}
}
return 0;
}
Because
envp no longer points to the current environment, this program has
unanticipated behavior.
Compliant Solution (Windows)
This compliant solution uses the
_environ variable in place of
envp:
#include <stdio.h>
#include <stdlib.h>
_CRTIMP extern char **_environ;
int main(int argc, const char *argv[]) {
if (_putenv_s("MY_NEW_VAR", "new_value") != 0) {
/* Handle error */
}
if (_environ != NULL) {
for (size_t i = 0; _environ[i] != NULL; ++i) {
puts(_environ[i]);
}
}
return 0;
}
Compliant Solution
This compliant solution can reduce remediation time when a large amount of
noncompliant
envp code exists. It replaces
int main(int argc, char *argv[], char *envp[]) {
/* ... */
}
with
#if defined (_POSIX_) || defined (__USE_POSIX)
extern char **environ;
#define envp environ
#elif defined(_WIN32)
_CRTIMP extern char **_environ;
#define envp _environ
#endif
int main(int argc, char *argv[]) {
/* ... */
}
This compliant solution may need to be extended to support other
implementations that support forms of the external variable
environ.
Risk Assessment
Using the
envp environment pointer after the environment has been modified
can result in
undefined
behavior.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| ENV31-C | Low | Probable | Medium | P4 | L3 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C | VOID ENV31-CPP. Do not rely on an environment pointer following an operation that may invalidate it | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
| [ IEEE Std 1003.1:2013] | XSH, System Interfaces,
setenv
|
| [ ISO/IEC 9899:2011] | J.5.1, "Environment Arguments" |
| [ MSDN] |
,
,
getenv,
_wgetenv,_putenv_s,
_wputenv_s
|
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
invalid_env_use |
Do not rely on an environment pointer following an operation that may invalidate it. |
None |
False |
Options
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
This rule has no individual options.