Understanding Coverity’s WRAPPER_ESCAPE Warning

Originally published in Chinese on 2020-03-15; this English edition preserves the original scope and technical context.

const char* Foo()
{
    std::string str_msg("test");
    return str_msg.c_str();
}

int main() {
    const char *p_msg = Foo();
    printf("%s\n", p_msg);
    return 0;
}

// output: (empty, or garbled)
D?

| Above code’s Foo function is reported with the coverity warning WRAPPER_ESCAPE. The detailed explanation is as follows:|

Above code, the Foo function reports a WRAPPER_ESCAPE warning from Coverity. The issue is detailed as follows:

Wrapper object use after free (WRAPPER_ESCAPE)
1. escape: The internal representation of local strMsg escapes, but is destroyed when it exits scope

The local variable str_msg, which is allocated on the stack within the function Foo, will be deallocated when it leaves the function (since str_msg is allocated on the stack). When the function std::string::c_str() is called to obtain a pointer to the beginning of str_msg, the returned pointer becomes a dangling pointer. Returning this dangling pointer to to the caller will result in unpredictable behavior.

While c_str() returns a const char* p, we cannot directly modify the data pointed to by the pointer p. However, we can achieve the effect of modifying the data pointed to by p by modifying str_msg, as shown in the following code:

int main() {
    std::string str_msg("test");
    const char *p_msg = str_msg.c_str();
    printf("%s\n", p_msg);
    str_msg[2] = 'x';
    printf("%s\n", p_msg);
    return 0;
}

// output:
test
text

To use the returned const char* correctly, we can allocate a block of memory on the heap, copy the string into it, and then return it:

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

char* safe_strdup(const char* str) { size_t len = strlen(str) + 1; char* copy = malloc(len); if (copy == NULL) { return NULL; // Error handling_strdup } memcpy(copy, str, len); return copy; }

const char* Foo()
{
    std::string str_msg("test");
    uint32_t u32_msg_size = str_msg.size() + 1;
    char *p_return = new char[u32_msg_size];
    strcpy_s(p_return, u32_msg_size, str_msg.c_str());
    return p_return;
}

int main() {
    const char *p_msg = Foo();
    printf("%s\n", p_msg);
    return 0;
}

// output:
test

Of course, the caller should delete the p_msg at appropriate times to avoid memory leaks.

What should be noted is that, unless you need to use strings immediately in the form of const char*, you should avoid using c_str() especially when passing them as function arguments.