Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki MarkupThe POSIX function {{putenv()}} is used to set environment variable values. The {{putenv()}} function does not create a copy of the string supplied to it as a parameter, rather it inserts a pointer to the string into the environment array. If an automatic variable is supplied as a parameter to {{putenv()}}, the memory allocated for that variable may be overwritten when the containing function returns and stack memory is recycled. This behavior is noted in the Open Group Base Specifications Issue 6 \[[Open Group 04|AA. C References#Open Group 04]\as an argument; rather, it inserts a pointer to the string into the environment array. If a pointer to a buffer of automatic storage duration is supplied as an argument to putenv(), the memory allocated for that buffer may be overwritten when the containing function returns and stack memory is recycled. This behavior is noted in the Open Group Base Specifications, Issue 6 [Open Group 2004]:

A potential error is to call putenv() with an automatic variable as the argument, then return from the calling function while string is still part of the environment.

...

.

The actual problem occurs when passing a pointer to an automatic variable to putenv(). An automatic pointer to a static buffer would work as intended.

Noncompliant Code Example

In this example taken from Dowd et al., an automatic variable noncompliant code example, a pointer to a buffer of automatic storage duration is used as an argument to putenv() [Dowd 2006]. The TEST environment variable may take on an unintended value if it is accessed once accessed after func() has returned and the stack frame containing env has been recycled.unmigrated-wiki-markup

Note that this example also violates rule \[[DCL30-C. Declare objects with appropriate storage durations]\].

Code Block
bgColor#FFCCCC
langc

int func(const char *var) {
  char env[1024];

  int retval if= (snprintf(env, sizeof(env),"TEST=%s", var);
  if (retval < 0 || (size_t)retval >= sizeof(env)) {
    /* Handle Errorerror */
  }

  return putenv(env);
}

Implementation Details

The putenv() function is not required to be reentrant, and the one in libc4, libc5 and glibc2.0 is not, but the glibc2.1 version is.

Description for libc4, libc5, glibc: If the argument string is of the form name, and does not contain an `=' character, then the variable name is removed from the environment. If putenv() has to allocate a new array environ, and the previous array was also allocated by putenv(), then it will be freed. In no case will the old storage associated to the environment variable itself be freed.

The libc4 and libc5 and glibc 2.1.2 versions conform to SUSv2: the pointer string given to putenv() is used. In particular, this string becomes part of the environment; changing it later will change the environment. (Thus, it is an error is to call putenv() with an automatic variable as the argument, then return from the calling function while string is still part of the environment.) However, glibc 2.0-2.1.1 differs: a copy of the string is used. On the one hand this causes a memory leak, and on the other hand it violates SUSv2. This has been fixed in glibc2.1.2.

The BSD4.4 version, like glibc 2.0, uses a copy.

SUSv2 removes the `const' from the prototype, and so does glibc 2.1.3.

The FreeBSD implementation of putenv() copies the value of the provided string and that old values remain accessible indefinitely. As a result, successive calls to putenv() assigning a differently sized value to the same name results in a memory leak.

Compliant Solution (static)

This compliant solution uses a static array for the argument to putenv().

Code Block
bgColor#ccccff
langc
int func(const char *var) {
  static char env[1024];

  int retval = snprintf(env, sizeof(env),"TEST=%s", var);
  if (retval < 0 || (size_t)retval >= sizeof(env)) {
    /* Handle error */
  }

  return putenv(env);
}

According to the [Open Group 2004] entry for putenv():

...the string pointed to by string shall become part of the environment, so altering the string shall change the environment.

This means that the call to putenv() is only necessary the first time func() is called, since subsequent changes to the string update the environment. If func() were called more than once, an additional variable could be added to avoid calling it unnecessarily.

Compliant Solution (Heap Memory)

This compliant solution dynamically allocates memory for the argument to putenv():

Code Block
bgColor#ccccff
langc
int func(const char *var) {
  const char *env_format = "TEST=%s";
  const size_t len = strlen(var) + strlen(env_format);
  char *env = (char *) malloc(len);
  if (env == NULL) {
    return -1;
  }
  int retval = snprintf(env, len, env_format, var);
  if (retval < 0 || (size_t)retval >= len) {
    /* Handle error */
  }
  if (putenv(env) != 0) {
    free(env);
    return -1;
  }
  return 0;
}

The POSIX setenv() function is preferred over this function [Open Group 2004]. In particular, using putenv() will necessarily leak memory if called multiple times for the same environment variable, due to restrictions on when you can safely free the old value. According to the [Open Group 2004] entry for putenv():

Although the space used by string is no longer used once a new string which defines name is passed to putenv(), if any thread in the application has used getenv() to retrieve a pointer to this variable, it should not be freed by calling free(). If the changed environment variable is one known by the system (such as the locale environment variables) the application should never free the buffer used by earlier calls to putenv() for the same variable.

Compliant Solution (setenv())

...

The setenv() function allocates heap memory for environment variables. This , which eliminates the possibility of accessing volatile , stack memory.:

Code Block
bgColor#ccccff
langc

int func(const char *var) {
  return setenv("TEST", var, 1);
}

Using setenv() is easier and consequently less error prone than using putenv().

Risk Assessment

Using an automatic variable Providing a pointer to a buffer of automatic storage duration as an argument to putenv() may cause that variable buffer to take on an unintended value. Depending on how and when that variable the buffer is used, this it can cause unexpected program behavior , or possibly allow an attacker to run arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Detectable

Repairable

Priority

Level

POS34-C

High

Unlikely

Yes

No

P6

L2

Automated Detection

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V

putenv-arg-local

Partially checked + soundly supported 
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC-POS34
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V
(customization)
BADFUNC.PUTENV
Users can add a custom check for all uses of putenv().
Use of putenv
Compass/ROSE




Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C5024
Klocwork

Include Page
Klocwork_V
Klocwork_V

CERT.PUTENV.AUTO_VARIABLE


Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-POS34-a
CERT_C

3 (high)

1 (unlikely)

2 (medium)

P6

L2

-POS34-b

Usage of system properties (environment variables) should be restricted
Do not call putenv() with a pointer to an automatic variable as the argument

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

2601

Fully supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule POS34-CChecks for use of automatic variable as putenv-family function argument (rule fully covered)
RuleChecker

Include Page
RuleChecker_V
RuleChecker_V

putenv-arg-localPartially checked

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

...

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-252/CWE-253/CWE-391 and ERR33-C/POS34-C

Independent( ERR33-C, POS54-C, FLP32-C, ERR34-C)

Intersection( CWE-252, CWE-253) = Ø

CWE-391 = Union( CWE-252, CWE-253)

CWE-391 = Union( ERR33-C, POS34-C, list) where list =

  • Ignoring return values of functions outside the C or POSIX standard libraries

Bibliography

[Dowd 2006]Chapter 10, "UNIX Processes"
[ISO/IEC 9899:2024]Section 6.2.4,

...

"Storage

...

Durations of Objects"
Section 7.24.3,

...

"Memory Management Functions"
[Open Group 2004]putenv()
setenv()


...

Image Added Image Added Image Added management functions" \[[Dowd et al.|AA. C References#Dowd 06]\] Chapter 10, "UNIX Processes" (Confusing putenv() and setenv()) \[[DCL30-C. Declare objects with appropriate storage durations]\]