CertC-POS34ΒΆ
Do not call putenv() with a pointer to an automatic variable as the argument
Required inputs: IR
The POSIX function
putenv() is used to set environment variable values. The
putenv() function does not create a copy of the string supplied to
it as an argument; rather, it inserts a pointer to the string into the
environment array. If a pointer to a buffer of automatic storage duration is
supplied as an argument to
putenv(), the memory allocated for that buffer may be overwritten
when the containing function returns and stack memory is recycled. This
behavior is noted in the Open Group Base Specifications, Issue 6 [
Open
Group 2004]:
A potential error is to call
putenv()with an automatic variable as the argument, then return from the calling function whilestringis still part of the environment.
The actual problem occurs when passing a pointer to an automatic
variable to
putenv(). An automatic pointer to a static buffer would work as
intended.
Noncompliant Code Example
In this noncompliant code example, a pointer to a buffer of automatic storage
duration is used as an argument to
putenv() [
Dowd
2006]. The
TEST environment variable may take on an unintended value if it is
accessed after
func() has returned and the stack frame containing
env has been recycled.
Note that this example also violates DCL30-C. Declare objects with appropriate storage durations.
int func(const char *var) {
char env[1024];
int retval = snprintf(env, sizeof(env),"TEST=%s", var);
if (retval < 0 || (size_t)retval >= sizeof(env)) {
/* Handle error */
}
return putenv(env);
}
Compliant Solution (
static)
This compliant solution uses a static array for the argument to
putenv().
int func(const char *var) {
static char env[1024];
int retval = snprintf(env, sizeof(env),"TEST=%s", var);
if (retval < 0 || (size_t)retval >= sizeof(env)) {
/* Handle error */
}
return putenv(env);
}
According to the [
Open
Group 2004] entry for
putenv():
...the string pointed to by string shall become part of the environment, so altering the string shall change the environment.
This means that the call to
putenv() is only necessary the first time
func() is called, since subsequent changes to the string update
the environment. If
func() were called more than once, an additional variable could be
added to avoid calling it unnecessarily.
Compliant Solution (Heap Memory)
This compliant solution dynamically allocates memory for the argument to
putenv():
int func(const char *var) {
const char *env_format = "TEST=%s";
const size_t len = strlen(var) + strlen(env_format);
char *env = (char *) malloc(len);
if (env == NULL) {
return -1;
}
int retval = snprintf(env, len, env_format, var);
if (retval < 0 || (size_t)retval >= len) {
/* Handle error */
}
if (putenv(env) != 0) {
free(env);
return -1;
}
return 0;
}
The POSIX
setenv() function is preferred over this function [
Open
Group 2004]. In particular, using
putenv() will necessarily leak memory if called multiple times for
the same environment variable, due to restrictions on when you can safely free
the old value. According to the [
Open
Group 2004] entry for
putenv():
Although the space used by string is no longer used once a new string which defines name is passed to putenv(), if any thread in the application has used getenv() to retrieve a pointer to this variable, it should not be freed by calling free(). If the changed environment variable is one known by the system (such as the locale environment variables) the application should never free the buffer used by earlier calls to putenv() for the same variable.
Compliant Solution (
setenv())
The
setenv() function allocates heap memory for environment variables,
which eliminates the possibility of accessing volatile stack memory:
int func(const char *var) {
return setenv("TEST", var, 1);
}
Using
setenv() is easier and consequently less error prone than using
putenv().
Risk Assessment
Providing a pointer to a buffer of automatic storage duration as an argument to
putenv() may cause that buffer to take on an unintended value.
Depending on how and when the buffer is used, it can cause unexpected program
behavior or possibly allow an attacker to run arbitrary code.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| POS34-C | high | unlikely | medium | P6 | L2 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|
Bibliography
| [ Dowd 2006] | Chapter 10, "UNIX Processes" |
| [ ISO/IEC 9899:2011] | Section 6.2.4, "Storage Durations of Objects" Section 7.22.3, "Memory Management Functions" |
| [ Open Group 2004] |
putenv()
setenv()
|
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
putenv_arg_pointing_to_local |
Do not call putenv() with a pointer to an automatic variable as the argument. |
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.