C99 defines getenv()
to have the following behavior: [[ISO/IEC 9899:1999]]
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 the getenv function.
Consequently, it is best not to store this pointer as 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 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, or copied so that the copy may be referenced safely at a later time.
The getenv()
function is not thread-safe. Make sure to address any possible race conditions resulting from the use of this function.
Noncompliant Code Example
This noncompliant code example compares the value of the TMP
and TEMP
environment variables to determine if they are the same. 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.
char *tmpvar; char *tempvar; tmpvar = getenv("TMP"); if (!tmpvar) return -1; tempvar = getenv("TEMP"); if (!tempvar) return -1; if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } }
Compliant Solution (Windows)
Windows provides the getenv_s()
and _wgetenv_s()
functions for getting a value from the current environment [[MSDN]].
char *tmpvar; char *tempvar; size_t requiredSize; getenv_s(&requiredSize, NULL, 0, "TMP"); tmpvar = (char *)malloc(requiredSize * sizeof(char)); if (!tmpvar) { /* Handle error */ } getenv_s(&requiredSize, tmpvar, requiredSize, "TMP" ); getenv_s(&requiredSize, NULL, 0, "TEMP"); tempvar = (char *)malloc(requiredSize * sizeof(char)); if (!tempvar) { free(tmpvar); tmpvar = NULL; /* Handle error */ } getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" ); if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution (Windows)
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()
.
It is the calling program's responsibility to free any allocated buffers returned by these functions.
char *tmpvar; char *tempvar; size_t len; errno_t err = _dupenv_s(&tmpvar, &len, "TMP"); if (err) return -1; errno_t err = _dupenv_s(&tempvar, &len, "TEMP"); if (err) { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution (POSIX)
POSIX provides the strdup()
function which can make a copy of the environment variable string [[Open Group 04]]. The strdup()
function is also included in ISO/IEC PDTR 24731-2 [[ISO/IEC PDTR 24731-2]].
char *tmpvar; char *tempvar; char *temp = getenv("TMP"); if (temp != NULL) { tmpvar = strdup(temp); if (tmpvar == NULL) { /* Handle Error */ } } else { return -1; } temp = getenv("TEMP"); if (temp != NULL) { tempvar = strdup(temp); if (tempvar == NULL) { free(tmpvar); tmpvar = NULL; /* Handle Error */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution
This compliant solution uses only the C99 malloc()
and strcpy()
functions to copy the string returned by getenv()
into a dynamically allocated buffer.
char *tmpvar; char *tempvar; char *temp = getenv("TMP"); if (temp != NULL) { tmpvar = (char *)malloc(strlen(temp)+1); if (tmpvar != NULL) { strcpy(tmpvar, temp); } else { /* Handle Error */ } } else { return -1; } temp = getenv("TEMP"); if (temp != NULL) { tempvar = (char *)malloc(strlen(temp)+1); if (tempvar != NULL) { strcpy(tempvar, temp); } else { free(tmpvar); tmpvar = NULL; /* Handle Error */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Risk Assessment
Storing the pointer to the string returned by getenv()
can result in overwritten environmental data.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
ENV00-C |
low |
probable |
medium |
P4 |
L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Automated Detection
Compass/ROSE could detect violations of this rule by reporting instances of two calls to getenv()
and seeing if the pointer that holds the result of the first getenv()
call is still referenced after the second getenv()
call.
References
[[ISO/IEC 9899:1999]] Section 7.20.4, "Communication with the environment"
[[ISO/IEC PDTR 24731-2]]
[[MSDN]] _dupenv_s()
and _wdupenv_s()
, getenv_s()
, _wgetenv_s()
[[Open Group 04]] Chapter 8, "Environment Variables", strdup
[[Viega 03]] Section 3.6, "Using Environment Variables Securely"