cstringconcatenation

Running strncat on MacOS 11.6.1


I am on MacOS 11.6.1 and want to append a character to a string and copied the following from there: https://www.geeksforgeeks.org/how-to-append-a-character-to-a-string-in-c/

I am supposed to have the following output:

Original String: Geek
Character to be appended: s
Appended String: Geeks

but only got the following one (with the last line missing)

Original String: Geek
Character to be appended: s

What am I doing wrong?

Here is the code

// C program to Append a Character to a String

#include <stdio.h>
#include <string.h>

int main()
{
    // declare and initialize string
    char str[6] = "Geek";

    // declare and initialize char
    char ch = 's';

    // print string
    printf("Original String: %s\n", str);
    printf("Character to be appended: %c\n", ch);

    // append ch to str
    strncat(str, &ch, 1);

    // print string
    printf("Appended String: %s\n", str);

    return 0;
}

Solution

  • This is a bug in strncat, specifically in the __builtin___strn_chk routine used to implement it. Consider this extract of the code in the question:

    char str[6] = "Geek";
    char ch = 's';
    strncat(str, &ch, 1);
    

    As it happens, the compiler puts ch immediately before str in memory. Examining the assembly output of compilation shows strncat is implemented with a call to __builtin___strn_chk. Stepping through __builtin___strn_chk in a debugger shows it calls __chk_overlap to check whether the source and destination buffers overlap. For this call, it passes the address of str with a length of six (the four characters in it plus the new character to be concatenated and a terminating null character after that) and the address of ch with a length of two.

    This length of two is incorrect; strncat or __builtin___strn_chk should take only one character from ch. This is a mistake in __builtin___strn_chk; it should pass a length of 1, not 2. __chk_overlap correctly determines the buffers it was passed overlap and generates a trap.

    This can be reliably reproduced with:

    #include <stdio.h>
    #include <string.h>
    
    
    int main(void)
    {
        char buffer[] = "xabc\0\0";
        strncat(buffer+1, buffer, 1);
    }
    

    __builtin___strncat_chk is used because <string.h> includes a header secure/_string.h which defines strncat as a macro:

    #define strncat(dest, ...) \
            __builtin___strncat_chk (dest, __VA_ARGS__, __darwin_obsz (dest))
    

    So we can work around the bug by replacing strncat with (strncat), which forces the strncat function to be used instead of the macro. One could also use #undef strncat after including <string.h>.