The C Standard, subclause 7.2224.4.6, paragraph 4 4 [ISO/IEC 9899:20112024] states that getenv()
has the following behavior:, states
The
getenv
function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program but may be overwritten by a subsequent call to thegetenv
function. If the specifiedname
cannot be found, a null pointer is returned.
This paragraph gives an implementation the latitude, for example, to return a pointer to a statically allocated buffer of static storage duration. Consequently, do not store this pointer because the string data it points to may be overwritten by a subsequent call to the getenv()
function or invalidated as a result of changes made by modifications to the environment list through calls to the POSIX functions putenv()
or setenv()
, or other implementation-specific means. Storing the pointer for later use can result in a dangling pointer or a pointer to incorrect data. This string should be referenced immediately and discarded; if . If later use is anticipated, the string should be copied so the copy can be safely referenced as needed.
The getenv()
function is not thread-safe. Make sure to address any possible race conditions resulting from the use of this function.
The asctime()
, localeconv()
, setlocale()
, and strerror()
functions have similar restrictions. Do not access the objects returned by any of these functions after a subsequent call.
...
This noncompliant code example compares attempts to compare the value of the TMP
and TEMP
environment variables to determine if they are the same:
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> #include <string.h> #include <stdio.h> void func(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 { /* Handle error */ } } else { /* Handle error */ } temp = getenv("TEMP"); if (temp != NULL) { tempvar = (char *)malloc(strlen(temp)+1); if (tempvar != NULL) { strcpy(tempvar, temp); } else { /* Handle error */ } } else { /* Handle error */ } 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; } |
Compliant Solution (Annex K)
The C Standard, Annex K, provides the getenv_s()
function for getting a value from the current environment. However, according to the standard, getenv_s()
can still have data races with other threads of execution that modify the environment list.
Code Block | ||||
---|---|---|---|---|
| ||||
#define __STDC_WANT_LIB_EXT1__ 1 #include <errno.h> #include <stdlib.h> #include <string.h> #include <stdio.h> void func(void) { char *tmpvar; char *tempvar; size_t requiredSize; errno_t err; err = getenv_s(&requiredSize, NULL, 0, "TMP"); if (err) { /* Handle error */ } tmpvar = (char *)malloc(requiredSize * sizeof(char)); if (!tmpvar) { /* Handle error */ } err = getenv_s(&requiredSize, tmpvar, requiredSize, "TMP" ); if (err) { /* Handle error */ } err = getenv_s(&requiredSize, NULL, 0, "TEMP"); if (err) { /* Handle error */ } tempvar = (char *)malloc(requiredSize * sizeof(char)); if (!tempvar) { /* Handle error */ } err = getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" ); if (err) { /* Handle error */ } 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; } |
...
Microsoft Windows provides the _dupenv_s()
and wdupenv_s()
functions for getting a value from the current environment [MSDN]. The _dupenv_s()
function searches the list of environment variables for a specified name. If the name is found, a buffer is allocated; the variable's value is copied into the buffer, and the buffer's address and number of elements are returned. By allocating the buffer itself, The _dupenv_s()
and _wdupenv_s()
functions provide a more convenient alternative alternatives to getenv_s()
and _wgetenv_s()
because each function handles buffer allocation directly.
The calling program caller is responsible for freeing any allocated buffers returned by these functions by calling free()
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
void func(void) {
char *tmpvar;
char *tempvar;
size_t len;
errno_t err = _dupenv_s(&tmpvar, &len, "TMP");
if (err) {
/* Handle error */
}
err = _dupenv_s(&tempvar, &len, "TEMP");
if (err) {
/* Handle error */
}
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;
}
|
Compliant Solution (POSIX or C2x)
POSIX provides the strdup()
function, which can make a copy of the environment variable string [IEEE Std 1003.1:2013]. The strdup()
function is also included in Extensions to the C Library—Part II [ISO/IEC TR 24731-2:2010]. Further, it is expected to be present in the C2x standard.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void func(void) {
char *tmpvar;
char *tempvar;
const char *temp = getenv("TMP");
if (temp != NULL) {
tmpvar = strdup(temp);
if (tmpvar == NULL) {
/* Handle error */
}
} else {
/* Handle error */
}
temp = getenv("TEMP");
if (temp != NULL) {
tempvar = strdup(temp);
if (tempvar == NULL) {
/* Handle error */
}
} else {
/* Handle error */
}
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;
}
|
...
Storing the pointer to the string returned by getenv()
, localeconv()
, setlocale()
, or strerror()
can result in overwritten data.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ENV34-C | Low | Probable | Medium | P4 | L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Automated Detection
Related Guidelines
Cppcheck Premium | 24.9.0 | premium-cert-env34-c | Fully implemented | ||||||
Helix QAC |
| DF2681, DF2682, DF2683 | |||||||
Klocwork |
| MISRA.STDLIB.ILLEGAL_REUSE.2012_AMD1 | |||||||
LDRA tool suite |
| 133 D | Fully implemented | ||||||
Parasoft C/C++test |
| CERT_C-ENV34-a | Pointers returned by certain Standard Library functions should not be used following a subsequent call to the same or related function | ||||||
| CERT C: Rule ENV34-C | Checks for misuse of return value from nonreentrant standard function (rule fully covered) |
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
C |
Secure Coding Standard | ENV00- |
C. Do not store |
objects that can be overwritten by multiple calls to getenv() and similar functions | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24731-2 |
5.3.1.1, "The strdup Function" | Prior to 2018-01-12: CERT: Unspecified Relationship | |
ISO/IEC TS 17961:2013 | Using an object overwritten by getenv , localeconv , setlocale , and strerror [libuse] | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
[IEEE Std 1003.1:2013] | Chapter 8, "Environment Variables" XSH, System Interfaces, strdup |
[ISO/IEC 9899: |
2024] | Subclause 7. |
24.4, "Communication with the Environment" Subclause 7. |
24.4.6, "The getenv Function"Subclause K.3.6.2.1, "The getenv_s Function" | |
[MSDN] | _dupenv_s() |
, _wdupenv_s() | |
[Viega 2003] | Section 3.6, "Using Environment Variables Securely" |
...
...