The C Standard, subclause 7.22.4.6, paragraph 4 [ISO/IEC 9899:2011] states that getenv()
has the following behavior:
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.
Consequently, it is best do not to store this pointer because it may be overwritten by a subsequent call to the getenv()
function or invalidated as a result of changes made 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 later use is anticipated, the string should be copied so the copy can be safely referenced as needed.
...
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.
...
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 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__ #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, _dupenv_s()
and _wdupenv_s()
provide a more convenient alternative to getenv_s()
and _wgetenv_s()
.
The calling program is responsible for freeing any allocated buffers returned by these functions by calling free()
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.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)
...
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 result in overwritten data.
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
|
|
|
...