Main Content

Misuse of return value from non-reentrant standard function

Pointer to static buffer from previous call is used despite a subsequent call that modifies the buffer

Description

This defect occurs when these events happen in this sequence:

  1. You point to the buffer returned from a nonreentrant standard function such as getenv or setlocale.

    user = getenv("USER");
  2. You call that nonreentrant standard function again.

    user2 = getenv("USER2");
  3. You use or dereference the pointer from the first step expecting the buffer to remain unmodified since that step. In the meantime, the call in the second step has modified the buffer.

    For instance:

    var=*user;

In some cases, the defect might appear even if you do not call the getenv function a second time but simply return the pointer. For instance:

char* func() {
     user=getenv("USER");
     .
     .
     return user;
}

For information on which functions are covered by this defect, see documentation on nonreentrant standard functions.

Risk

The C Standard allows nonreentrant functions such as getenv to return a pointer to a static buffer. Because the buffer is static, a second call to getenv modifies the buffer. If you continue to use the pointer returned from the first call past the second call, you can see unexpected results. The buffer that it points to no longer has values from the first call.

The defect appears even if you do not call getenv a second time but simply return the pointer. The reason is that someone calling your function might use the returned pointer after a second call to getenv. By returning the pointer from your call to getenv, you make your function unsafe to use.

The same rationale is true for other nonreentrant functions covered by this defect.

Fix

After the first call to getenv, make a copy of the buffer that the returned pointer points to. After the second call to getenv, use this copy. Even if the second call modifies the buffer, your copy is untouched.

Examples

expand all

#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");   /* First call */ 
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/');
 
        if (user_name_from_home != NULL) {
            user = getenv("USER");   /* Second call */
            if ((user != NULL) &&
                (strcmp(user, user_name_from_home) == 0)) 
            {
                result = 1;
            }
        }
    }
    return result;
}

In this example, the pointer user_name_from_home is derived from the pointer home. home points to the buffer returned from the first call to getenv. Therefore, user_name_from_home points to a location in the same buffer.

After the second call to getenv, the buffer is modified. If you continue to use user_name_from_home, you can get unexpected results.

Correction — Make Copy of Buffer Before Second Call

If you want to access the buffer from the first call to getenv past the second call, make a copy of the buffer after the first call. One possible correction is to use the strdup function to make the copy.

#include <stdlib.h>
#include <string.h>

int func()
{
    int result = 0;

    char *home = getenv("HOME");    
    if (home != NULL) {
        char *user = NULL;
        char *user_name_from_home = strrchr(home, '/'); 
        if (user_name_from_home != NULL) {
            /* Make copy before second call */
            char *saved_user_name_from_home = strdup(user_name_from_home); 
            if (saved_user_name_from_home != NULL) {
                user = getenv("USER");  
                if ((user != NULL) &&
                    (strcmp(user, saved_user_name_from_home) == 0)) 
                {
                    result = 1;
                }
                free(saved_user_name_from_home);
            }
        }
    }
    return result;
}

Result Information

Group: Programming
Language: C | C++
Default: Off
Command-Line Syntax: NON_REENTRANT_STD_RETURN
Impact: High

Version History

Introduced in R2017a