The C Standard, 7.24.4.6, paragraph 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.
Consequently, it is best not to This paragraph gives an implementation the latitude, for example, to return a pointer to a statically allocated buffer. 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 putenv()
, setenv()
, or other 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 caveatsrestrictions. Do not access the objects returned by any of these functions after a subsequent call.
Noncompliant Code Example
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; tmpvar = getenv("TMP"); if (!tmpvar) { /* Handle return -1; error */ } tempvar = getenv("TEMP"); if (!tempvar) { /* Handle return -1; error */ } if (strcmp(tmpvar, tempvar) == 0) { if (putsprintf("TMP and TEMP are the same.\n") == EOF) {; /* Handle error */ } } } else { if (putsprintf("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle error */; } } |
This code example is noncompliant because the string referenced by tmpvar
may be overwritten as a result of the second call to the getenv()
function. As a result, it is possible that both tmpvar
and tempvar
will compare equal even if the two environment variables have different values.
Compliant Solution
This compliant solution uses only the C the malloc()
and strcpy()
functions to copy the string returned by getenv()
into a dynamically allocated buffer:
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)
C11 The C Standard, Annex K, provides the getenv_s()
function for getting a value from the current environment. However, note that 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; } |
Compliant Solution (Windows)
Microsoft Windows also 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; } |
Risk Assessment
Storing the pointer to the string returned by getenv()
, {{localeconv()}}
, {{setlocale()}}
, or {{strerror()}}
can 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" |
...
...