The POSIX function {{Wiki Markup 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 04|AA. C References#Open Group 04]\]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.
...
Noncompliant Code Example
In this noncompliant code example, a pointer to a buffer of automatic storage duration is used as an argument to {{Wiki Markup putenv()}} \ [[Dowd 06|AA. C References#Dowd 06]\]. The {{TEST}} environment variable may take on an unintended value if it is accessed once {{func()}} has returned and the stack frame containing {{env}} has been 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 rule DCL30-C. Declare objects with appropriate storage durations.
| Code Block | ||||
|---|---|---|---|---|
| ||||
int func(const char *var) { char env[1024]; int retval if= (snprintf(env, sizeof(env),"TEST=%s", var); if (retval < 0 || (size_t)retval >= sizeof(env)) { /* Handle Errorerror */ } return putenv(env); } |
Compliant Solution (
...
static)
This compliant solution uses a static array for the argument to putenv().
| Code Block | ||||
|---|---|---|---|---|
| ||||
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():
| Code Block | ||||
|---|---|---|---|---|
| ||||
int func(const char *var) { *oldenv; 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 rcretval = snprintf(env, len, env_format, var); if (rcretval < 0 || (size_t)rcretval >= len) { /* Handle Errorerror */ } if (putenv(env) != 0) { free(env); return -1; } if (oldenv != NULL) free(oldenv); /* avoid memory leak */ oldenv = env; return 0; } |
The POSIX {{Wiki Markup setenv()}} function is preferred over this function \ [[Open Group 04|AA. C References#Open Group 04]\]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. This , which eliminates the possibility of accessing volatile stack memory.:
| Code Block | ||||
|---|---|---|---|---|
| ||||
int func(const char *var) {
return setenv("TEST", var, 1);
}
|
Using setenv() is easier , and consequently less error prone , than using ( putenv()).
Risk Assessment
Using 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, this it can cause unexpected program behavior , or possibly allow an attacker to run arbitrary code.
Rule | Severity | Likelihood |
|---|
Detectable | Repairable | Priority | Level |
|---|---|---|---|
POS34-C | High |
Unlikely |
Yes |
No | P6 | L2 |
Automated Detection
Fortify SCA Version 5.0 with CERT C Rule Pack can detect violations of this rule.
...
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| putenv-arg-local | Partially checked + soundly supported | ||||||
| Axivion Bauhaus Suite |
| CertC-POS34 | |||||||
| CodeSonar |
| (customization) BADFUNC.PUTENV | Users can add a custom check for all uses of putenv().Use of putenv | ||||||
| Compass/ROSE | |||||||||
| Helix QAC |
| C5024 | |||||||
| Klocwork |
| CERT.PUTENV.AUTO_VARIABLE | |||||||
| Parasoft C/C++test |
| CERT_C-POS34-a | Usage of system properties (environment variables) should be restricted | ||||||
| PC-lint Plus |
| 2601 | Fully supported | ||||||
| CERT C: Rule POS34-C | Checks for use of automatic variable as putenv-family function argument (rule fully covered) | |||||||
| RuleChecker |
| putenv-arg-local | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
...
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
|---|
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-252/CWE-253/CWE-391 and ERR33-C/POS34-C
Independent( ERR33-C, POS54-C, FLP32-C, ERR34-C)
Intersection( CWE-252, CWE-253) = Ø
CWE-391 = Union( CWE-252, CWE-253)
CWE-391 = Union( ERR33-C, POS34-C, list) where list =
- Ignoring return values of functions outside the C or POSIX standard libraries
Bibliography
| [Dowd 2006] | Chapter 10, "UNIX Processes" |
| [ISO/IEC 9899:2024] | Section 6.2.4, |
...
| "Storage |
...
| Durations of Objects" Section 7.24.3, |
...
| "Memory Management Functions" | |
| [Open Group 2004] | putenv()setenv() |
...
management functions" \[[Dowd 06|AA. C References#Dowd 06]\] Chapter 10, "UNIX Processes" (Confusing putenv() and setenv())POS33-C. Do not use vfork() 50. POSIX (POS)