C99 defines {{getenv()}} to have the following behavior: \[The C Standard, 7.24.4.6, paragraph 4 [ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\]2024], states Wiki Markup
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 store this pointer as it 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, or . If later use is anticipated, the string should be copied so that the copy may can be referenced safely at a later timesafely 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.
Noncompliant Code Example
This non-compliant 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 error */ } tempvar = getenv("TEMP"); if (!tempvar) { /* 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"); } } |
This code example is non-compliant 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 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; tmpvar const char *temp = getenv("TMP"); if (temp != NULL) { tmpvar = (char *)malloc(strlen(temp)+1); if (tmpvar != NULL) return -1; tempvar { strcpy(tmpvar, temp); } else { /* Handle error */ } } else { /* Handle error */ } temp = getenv("TEMP"); if (temp != NULL) { tempvar = (char *)malloc(strlen(temp) return -1; +1); if (tempvar != NULL) { strcpy(tempvar, temp); } else { /* Handle error */ } } else { /* Handle error */ } if (strcmp(tmpvar, tempvar) == 0) { puts printf("TMP and TEMP are the same.\n"); } else { puts printf("TMP and TEMP are NOT the same.\n"); } free(tmpvar); free(tempvar); } |
Compliant Solution (
...
Annex K)
The C Standard, Annex K, provides the Windows provides the [{{ Wiki Markup getenv_s()
}} and {{\_wgetenv_s()}}|http://msdn.microsoft.com/en-us/library/tb2sfw2z(VS.80).aspx] functions for getting a value from the current environment \[[MSDN|AA. C References#MSDN]\] function for getting a value from the current environment. However, 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) { free(tmpvar); tmpvar = NULL; /* Handle error */ } err = getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" ); if (err) { /* Handle error */ } if (strcmp(tmpvar, tempvar) == 0) { puts printf("TMP and TEMP are the same.\n"); } else { puts printf("TMP and TEMP are NOT the same.\n"); } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL; } |
Compliant Solution (Windows)
Windows also provides the [{{\Microsoft Windows provides the Wiki Markup _dupenv_s()
}} and {{\_wdupenv_s()
}}|http://msdn.microsoft.com/en-us/library/ms175774.aspx] functions for getting a value from the current environment \ [[MSDN|AA. C References#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.
It is the calling program's responsibility to free The 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) return -1; errno_t { /* Handle error */ } err = _dupenv_s(&tempvar, &len, "TEMP"); if (err) { free(tmpvar); tmpvar = NULL; return -1; } /* Handle error */ } if (strcmp(tmpvar, tempvar) == 0) { putsprintf("TMP and TEMP are the same.\n"); } else { puts 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 [{{ Wiki Markup strdup()
}}|http://www.opengroup.org/onlinepubs/009695399/functions/strdup.html] function which can make a copy of the environment variable string \[[Open Group 04|AA. C References#Open Group 04]\]. The {{strdup()}} function is also included in ISO/IEC PDTR 24731-2 \[[ISO/IEC PDTR 24731-2|AA. C References#ISO/IEC ISO/IEC PDTR 24731-2]\] 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> char *tmpvar; char *tempvar; char *temp = getenv("TMP"); if (temp != NULL#include <string.h> #include <stdio.h> void func(void) { char *tmpvar = strdup(temp); if (tmpvar == NULL) { /* Handle Error */ } } else { return -1; } char *tempvar; const char *temp = getenv("TEMPTMP"); if (temp != NULL) { tempvar tmpvar = strdup(temp); if (tempvartmpvar == NULL) { free(tmpvar); tmpvar = NULL; /* Handle Errorerror */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { puts("TMP and TEMP are the same.\n"); } else { puts("TMP and TEMP are NOT the same.\n"); } 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.
Code Block | ||
---|---|---|
| ||
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 Errorerror */ } } else { return -1; } temp = getenv("TEMP"); if (temp != NULL) { tempvar = (char *)malloc(strlenstrdup(temp)+1); if (tempvar !== NULL) { strcpy(tempvar, temp); /* } Handle error else {*/ free(tmpvar);} } tmpvar = NULL;else { /* Handle Errorerror */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { putsprintf("TMP and TEMP are the same.\n"); } else { putsprintf("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 environmental 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.
References
Wiki Markup |
---|
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.4, "Communication with the environment"
\[[ISO/IEC PDTR 24731-2|AA. C References#ISO/IEC PDTR 24731-2-2007]\]
\[[MSDN|AA. C References#MSDN]\] [{{\_dupenv_s()}} and {{\_wdupenv_s()}}|http://msdn.microsoft.com/en-us/library/ms175774.aspx], [{{getenv_s()}}, {{\_wgetenv_s()}}|http://msdn.microsoft.com/en-us/library/tb2sfw2z(VS.80).aspx]
\[[Open Group 04|AA. C References#Open Group 04]\] Chapter 8, "Environment Variables", [strdup|http://www.opengroup.org/onlinepubs/009695399/functions/strdup.html]
\[[Viega 03|AA. C References#Viega 03]\] Section 3.6, "Using Environment Variables Securely" |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Compass/ROSE | |||||||||
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" |
...
10. Environment (ENV) 10. Environment (ENV) ENV01-A. Do not make assumptions about the size of an environment variable