Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated references from C11->C23

Wiki Markup
The {{getenv()}} function searches an environment list, provided by the host environment, for a string that matches a specified name.  The {{getenv()}} function returns a pointer to a string associated with the matched list member.  It is best not to store this pointer as it may be overwritten by a subsequent call to the {{getenv()}} function \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] or invalidated as a result of other manipulation of the environment list.

Wiki MarkupAccording to C99 \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\]:The C Standard, 7.24.4.6, paragraph 4 [ISO/IEC 9899:2024], 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 the getenv function. If the specified name cannot be found, a null pointer is returned.

This allows paragraph gives an implementation the latitude, for example, to copy the environmental variable to an internal static buffer and return a pointer to that a statically allocated buffer.

If you do not immediately make a copy of the value returned by getenv(), but instead store the pointer somewhere for later use, you could end up with a dangling pointer or a different value altogether.

Implementation Details

According to the Microsoft Visual Studio 2005/.NET Framework 2.0 help pages http://msdn2.microsoft.com/en-us/library/tehxacec(VS.80).aspx:Image Removed

The getenv function searches the list of environment variables for varname. getenv is not case sensitive in the Windows operating system. getenv and _putenv use the copy of the environment pointed to by the global variable _environ to access the environment. getenv operates only on the data structures accessible to the run-time library and not on the environment "segment" created for the process by the operating system. Therefore, programs that use the envp argument to main or wmain may retrieve invalid information.

Non-Compliant Coding Example

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 by modifications to the environment. 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.

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 noncompliant code example attempts to compare the value of the TMP and TEMP environment variables to determine if they are the same:

Code Block
bgColor#FFcccc
langc
#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
Code Block
bgColor#FFcccc

char *pwd;
char *home;
  
pwd = getenv("PWD");
if (!pwd) return -1;
home = getenv("HOME");
if (!home) return -1;

if (strcmp(pwd, home) == 0) {
  puts  printf("pwdTMP and homeTEMP are the same.\n");
  }
 else {
  puts  printf("pwdTMP and homeTEMP are NOT the same.\n");
    }
}

Compliant Solution (Windows)

Microsoft Visual Studio 2005 provides provides the ((_dupenv_s()}} and _wdupenv_s() functions for getting a value from the current environment. http://msdn2.microsoft.com/en-us/library/ms175774(VS.80).aspxImage Removed

The ((_dupenv_s()}} function searches the list of environment variables for varname. If the variable is found, a buffer is allocated, the variable's value is copied into the buffer, and the buffer's address and length are returned in buffer and numberOfElements. By allocating the buffer itself, ((_dupenv_s()}} provides a more convenient alternative to getenv_s, _wgetenv_s.

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 the malloc() and strcpy() functions to copy the string returned by getenv() into a dynamically allocated buffer:It is the calling program's responsibility to free the memory by calling free)_.

Code Block
bgColor#ccccff
langc
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
 
void func(void) {
  char *pValuetmpvar;
   size_t lenchar *tempvar;

  const errno_t errchar *temp = _dupenv_s( &pValue, &len, "pathext" );
getenv("TMP");
  if (temp != NULL) {
    tmpvar = (char *)malloc(strlen(temp)+1);
    if (tmpvar err!= NULL) return -1;
   printf( "pathext = %s\n", pValue );
   free( pValue {
      strcpy(tmpvar, temp);
    } else {
      /* Handle error */
    }
  } else {
    /* Handle error */
  }

  temp = getenv("TEMP");
  if (temp != NULL) {
    tempvar = (char *)malloc(strlen(temp)+1);
   err if (tempvar != _dupenv_s( &pValue, &len, "nonexistentvariable" );
   if ( err ) return -1;
   printf( "nonexistentvariable = %s\n", pValue );
   free( pValue ); // It's OK to call free with NULL

Compliant Solution (Windows)

 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);
  free(tempvar);
}

Compliant Solution (Annex K)

The C Standard, Annex K, provides the getenv_s() 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 listMicrosoft Visual Studio 2005 provides provides the ((getenv_s()}} and _wgetenv_s() functions for getting a value from the current environment.

Code Block
bgColor#ccccff
langc
#define __STDC_WANT_LIB_EXT1__ 1
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
 
intvoid mainfunc( void ) {
  char *tmpvar;
  char * libvartempvar;
   size_t requiredSize;
  errno_t err;
  err = getenv_s( &requiredSize, NULL, 0, "LIBTMP");

   libvarif (err) {
    /* Handle error */
  }
 
  tmpvar = (char *) malloc(requiredSize * sizeof(char));
   if (!libvartmpvar) {
    /* Handle error {*/
  }
  err = printf("Failed to allocate memory!\n");
      exit(1);
   }

   // Get the value of the LIB environment variable.
   getenv_s( &requiredSize, libvargetenv_s(&requiredSize, tmpvar, requiredSize, "TMP" );

  if (err) {
    /* Handle error */
  }
  err = getenv_s(&requiredSize, NULL, 0, "TEMP");
  if (err) {
    /* Handle error */
  }
 
  tempvar = (char *)malloc(requiredSize);
  if (!tempvar) {
    /* Handle error */
  }
  err = getenv_s(&requiredSize, tempvar, requiredSize, "LIBTEMP" );

  if (err) {
    /* Handle error */
  }
  if ( libvar != NULL )
      printf( "Original LIB variable is: %s\n", libvar );

   // Attempt to change path. Note that this only affects
   // the environment variable of the current process. The command
   // processor's environment is not changed.
   _putenv_s( "LIB", "c:\\mylib;c:\\yourlib" );

   getenv_s( &requiredSize, NULL, 0, "LIB");

   libvar = (char*) malloc(requiredSize * sizeof(char));
   if (!libvar) {
      printf("Failed to allocate memory!\n");
      exit(1);
   }

   // Get the new value of the LIB environment variable. 
   getenv_s( &requiredSize, libvar, requiredSize, "LIB" );

   if( libvar != NULL )
      printf( "New LIB variable is: %s\n", libvar );
}

There is a race condition here even after you call getenv() and before you copy. Be careful to only manipulate the process environment from a single thread at a time.

Compliant Solution (POSIX)

The following compliant solution depends on the POSIX strdup() function to make a copy of the environment variable string.

Code Block
bgColor#ccccff

char *tmpvar = strdup(getenv("TMP"));

If the TMP environmental variable returns does not exist, the call to getenv() returns NULL. In these cases, the call to strdup() should also return NULL, but it is important to verify this as this behavior is not guaranteed by POSIX OpenGroup 05

Risk Assessment

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 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. The _dupenv_s() and _wdupenv_s() functions provide more convenient alternatives to getenv_s() and _wgetenv_s() because each function handles buffer allocation directly.

The caller is responsible for freeing any allocated buffers returned by these functions by calling free().

Code Block
bgColor#ccccff
langc
#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
bgColor#ccccff
langc
#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.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ENV03

ENV34-

A

2 (high)

2 (probable)

2 (medium)

P8

L2

C

Low

Probable

Medium

P4

L3

Related Vulnerabilities

Search for Examples of vulnerabilities resulting from the violation of this recommendation can be found rule on the CERT website.

Automated Detection

Tool

Version

Checker

Description

Compass/ROSE




Cppcheck Premium
24.9.0
premium-cert-env34-c

Fully implemented

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF2681, DF2682, DF2683


Klocwork
Include Page
Klocwork_V
Klocwork_V

MISRA.

...

STDLIB.ILLEGAL_REUSE.2012_AMD1


LDRA tool suite
Include Page
LDRA_V
LDRA_V

133 D

Fully implemented
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

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

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule ENV34-CChecks 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 StandardENV00-C. Do not store objects that can be overwritten by multiple calls to getenv() and similar functionsPrior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24731-25.3.1.1, "The strdup Function"Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013Using 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"


...

Image Added Image Added Image Added

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" \[[Viega 03|AA. C References#Viega 03]\] Section 3.6, "Using Environment Variables Securely" \[[Wheeler 03|AA. C References#Wheeler 03]\] [Section 5.2, "Environment Variables"|http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html] \[[Open Group 04|AA. C References#Open Group 04]\] [strdup|http://www.opengroup.org/onlinepubs/009695399/functions/strdup.html]