Search code examples
linuxgccglibcstrncpyfortify-source

strncpy is replaced by __strncpy_chk and fails


I have a statement

strncpy(&data->m_bin->data,versionStr,data->m_bin->sizeData);

in my application which itself is fine and works well. Here data->m_bin->data is a char where the calling application ensures it is followed by a datablock which is large enough to keep all the data handed over by strncpy().

But when I build this as release using GCC/Linux, this function crashes in __strncpy_chk(). So it seems my strncpy() was replaced by __strncpy_chk() using a wrong length for parameter s1.

So how can I ensure __strncpy_chk() is called with the correct length for s1?

Thanks!


Solution

  • strncpy(&data->m_bin->data,versionStr,data->m_bin->sizeData);

    The address of operator looks suspicious to me. I would expect something like:

    strncpy(data->m_bin->data,versionStr,data->m_bin->sizeData);
    

    Or maybe:

    strncpy(&data->m_bin->data[0],versionStr,data->m_bin->sizeData);
    

    how can I ensure __strncpy_chk() is called with the correct length for s1?

    Well, you can't per se. This is part of FORTIFY_SOURCE and object size checking, and the destination buffer size is used when the compiler can deduce it.

    You could possibly do something like the following, assuming data is an array of size sizeData.

    /* avoid undefined behavior */
    ASSERT(data->m_bin->data != NULL);
    ASSERT(versionStr != NULL);
    ASSERT(data->m_bin->sizeData > 0);
    
    size_t l1 = data->m_bin->sizeData;
    size_t l2 = strlen(versionStr);
    
    /* min function */
    size_t len = l1 < l2 ? l1 : l2;
    
    /* if versionStr is shorter than len, then data will be backfilled */
    strncpy(data->m_bin->data, versionStr, len);
    
    /* NULL terminate, even if it truncates */
    data->m_bin->data[data->m_bin->sizeData-1] = '\0';
    

    You should probably turn on warnings with -Wall. I suspect you should get one for using the address of operator.