Do not make any assumptions about the size of environment variables . Calculate because an adversary might have full control over the environment. If the environment variable needs to be stored, the length of the strings yourself, and dynamically allocate memory for your copies. There is nothing you can do to avoid the race conditions inherent here, but you can limit your exposure.
Non-Compliant Coding Example
associated string should be calculated and the storage dynamically allocated (see STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator).
Noncompliant Code Example
This noncompliant code example copies the string returned by getenv()
into a fixed-size buffer:This non-compliant code example copies into a buffer of fixed size.
Code Block | ||
---|---|---|
| ||
//todo
|
Compliant Solution (Windows)
Microsoft Visual Studio 2005 provides provides the getenv_s()
and _wgetenv_s()
functions for getting a value from the current environment.
Code Block | ||
---|---|---|
| ||
char *tmpvar;
char *tempvar;
size_t requiredSize;
getenv_s(&requiredSize, NULL, 0, "TMP");
tmpvar= malloc(requiredSize * sizeof(char));
if (!tmpvar) {
/* handle error condition */
}
getenv_s(&requiredSize, tmpvar, requiredSize, "TMP" );
getenv_s(&requiredSize, NULL, 0, "TEMP");
tempvar= malloc(requiredSize * sizeof(char));
if (!tempvar) {
/* handle error condition */
}
getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" );
if (strcmp(tmpvar, tempvar) == 0) {
puts("TMP and TEMP are the same.\n");
}
else {
puts("TMP and TEMP are NOT the same.\n");
}
|
Compliant Solution (Windows)
Wiki Markup |
---|
Microsoft Visual Studio 2005 provides provides the {{\_dupenv_s()}} and {{\_wdupenv_s()}} functions for getting a value from the current environment. \[[Microsoft Visual Studio 2005/.NET Framework 2.0 help pages|http://msdn2.microsoft.com/en-us/library/ms175774(VS.80).aspx]\]. |
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()
provides a more convenient alternative to getenv_s()
, _wgetenv_s()
.
It is the calling program's responsibility to free the memory by calling free()
.
Code Block | ||
---|---|---|
| ||
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);
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);
free(tempvar);
|
Compliant Solution (POSIX)
The following compliant solution depends on the POSIX strdup()
function to make a copy of the environment variable string.
Code Block | ||
---|---|---|
| ||
char *tmpvar = strdup(getenv("TMP"));
char *tempvar = strdup(getenv("TEMP"));
if (!tmpvar) return -1;
if (!tempvar) 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");
}
|
Wiki Markup |
---|
If an environmental variable does not exist, the call to {{getenv()}} returns a null pointer. In these cases, the call to {{strdup()}} should also return a null pointer, but it is important to verify this as this behavior is not guaranteed by POSIX \[[Open Group 04|AA. C References#Open Group 04]\] |
Compliant Solution
This compliant solution is fully portable.
| ||
void f() {
char path[PATH_MAX]; /* Requires PATH_MAX to be defined */
strcpy(path, getenv("PATH"));
/* Use path */
}
|
Even if your platform assumes that $PATH
is defined, defines PATH_MAX
, and enforces that paths not have more than PATH_MAX
characters, the $PATH
environment variable still is not required to have less than PATH_MAX
chars. And if it has more than PATH_MAX
chars, a buffer overflow will result. Also, if $PATH
is not defined, then strcpy()
will attempt to dereference a null pointer.
Compliant Solution
In this compliant solution, the strlen()
function is used to calculate the size of the string, and the required space is dynamically allocated:
Code Block | ||||
---|---|---|---|---|
| ||||
void f() {
char *path = NULL;
/* Avoid assuming $PATH is defined or has limited length */
const char *temp = getenv("PATH");
if (temp != NULL) {
path = (char*) malloc(strlen(temp) + 1);
if (path == NULL) {
/* Handle error condition */
} else {
strcpy(path, temp);
}
/* Use path */
free(path);
}
}
|
Compliant Solution (POSIX or C2x)
In this compliant solution, the strdup()
function is used to dynamically allocate a duplicate of the string:
Code Block | ||||
---|---|---|---|---|
| ||||
void f() {
char *path = NULL;
/* Avoid assuming $PATH is defined or has limited length */
const char *temp = getenv("PATH");
if (temp | ||||
Code Block | ||||
| ||||
char *tmpvar; char *tempvar; char *temp; if ( (temp = getenv("TMP")) != NULL) { tmpvar= malloc(strlen(temp)+1); if (tmpvar != NULL) { strcpy(tmpvar, temp); } else { /* handle error condition */ } } else { return -1; } if ( (temp = getenv("TEMP")) != NULL) { path tempvar= malloc(strlenstrdup(temp)+1); if (tempvarpath !== NULL) { strcpy(tempvar, temp); } else { /* handleHandle error condition */ } } else { 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"); } |
Risk Assessment
/* Use path */
free(path);
}
} |
Risk Assessment
Making assumptions about the size of an environmental variable can result in a buffer overflow.
Recommendation |
---|
Severity | Likelihood | Remediation Cost | Priority | Level |
---|
ENV00-A
1 (low)
1 (low)
2 (medium)
P8
L2
ENV01-C | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| LANG.MEM.BO | Buffer overrun | ||||||
Compass/ROSE | Can detect violations of the rule by using the same method as STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator | ||||||||
Klocwork |
| ABV.ANY_SIZE_ARRAY ABV.GENERAL ABV.GENERAL.MULTIDIMENSION ABV.ITERATOR ABV.MEMBER ABV.STACK ABV.TAINTED ABV.UNKNOWN_SIZE ABV.UNICODE.BOUND_MAP ABV.UNICODE.FAILED_MAP ABV.UNICODE.NNTS_MAP ABV.UNICODE.SELF_MAP | |||||||
Parasoft C/C++test |
| CERT_C-ENV01-a | Don't use unsafe C functions that do write to range-unchecked buffers | ||||||
PC-lint Plus |
| 669 | Fully supported | ||||||
Polyspace Bug Finder |
| Checks for tainted NULL or non-null-terminated string (rec. partially covered) |
Related Vulnerabilities
Search for Examples of vulnerabilities resulting from the violation of this recommendation can be found 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"
\[[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" |
Related Guidelines
MITRE CWE | CWE-119, Improper Restriction of Operations within the Bounds of a Memory Buffer CWE-123, Write-what-where Condition CWE-125, Out-of-bounds Read |
Bibliography
[IEEE Std 1003.1:2013] | Chapter 8, "Environment Variables" |
[Viega 2003] | Section 3.6, "Using Environment Variables Securely" |
...
http://it-reading.org/book/secure%20programming%20cookbook%20c%20and%20c++/content/0596003943_secureprgckbk-chp-3-sect-6.html