-1

For an assignment, I am building a replaceString function with using the 3 functions I already built (findString, removeString, insertString) that replaces the first occurrence of s1 string inside the source string with s2 string. It came out fine, except when s1 is a single character (ex. "a") or a null string "", an error "stack smashing detected" will show below it:

hello,  world
*** stack smashing detected ***: /home/noob/ex1008 terminated
======= Backtrace: =========
...
======= Memory map: ========
...

And this happens when I call the function like this:

char text[] = "a world";
replaceString ( text, "a", "hello," );

or

char text[] = " world";
replaceString ( text, "", "hello," );

However, this error can be eliminated when I declare array length like this:

char text[10] = "a world";
replaceString ( text, "a", "hello," )

Or if the length of s1 is more than 1:

char text[] = "the world";
replaceString ( text, "the", "hello," )

I don't understand why it acts like this. Is there a way that I don't need to declare array length when I input single character string or null string? See my whole code below:

#include    <stdlib.h>
#include    <stdio.h>

int  main ( int  argc, char  *argv[] )
{
    char text[] = "a world";
    void replaceString ( char source[], const char s1[], const char s2[] );

    replaceString ( text, "a", "hello," );

    printf ( "%s\n", text );

    return EXIT_SUCCESS;
}       /* ----------  end of function main  ---------- */

/* 
 * ===  FUNCTION  =========================================================
 *         Name:  replaceString
 *  Description:  Replaces first occurrence of s1 in source with s2.
 * ========================================================================
 */

void  replaceString ( char  source[], const char  s1[], const char  s2[] )
{
    int start, removeLength;
    int findString ( const char source[], const char search[] );
    void removeString ( char source[], const int start, const int remove );
    void insertString ( char source[], const char insert[], 
            const int start );

    start = findString ( source, s1 );
    if ( start == -1 )
        return;

    for ( removeLength = 0; s1[removeLength]; removeLength++ );

    removeString ( source, start, removeLength );
    insertString ( source, s2, start );

}       /* -----  end of function replaceString  ----- */
/* 
 * ===  FUNCTION  =========================================================
 *         Name:  findString
 *  Description:  Determines if one character string exists inside other
 *                string. If found, returns location in source string. If
 *                not, returns -1.
 * ========================================================================
 */

int  findString ( const char  source[], const char  search[] )
{
    int     i, j;

    for ( i = 0; source[i]; i++ )   
        for ( j = 0; source[i + j] == search[j] || ! search [j]; j++ )
            if ( ! search[j] ) 
                return i;

    return -1;
}       /* -----  end of function findString  ----- */

/* 
 * ===  FUNCTION  =========================================================
 *         Name:  removeString
 *  Description:  Removes a specified number of characters from a character
 *                string.
 * ========================================================================
 */

void  removeString ( char  source[], const int  start,
        const int  remove )
{
    char    output[80];
    int     i, j = 0;

    for ( i = 0; source[i]; i++ )
        if ( i < start || i >= start + remove )
            output[j++] = source[i];

    output[j] = '\0';

    for ( i = 0; output[i]; i++)
        source[i] = output[i];

    source[i] = '\0';
}       /* -----  end of function removeString  ----- */

/* 
 * ===  FUNCTION  =========================================================
 *         Name:  insertString
 *  Description:  Inserts a string into another string.
 * ========================================================================
 */

void  insertString ( char  source[], const char  insert[], 
                     const int  start )
{
    char    output[80];
    int     i = 0, j = 0, k = 0;

    while ( source[i] ) {
        if ( i < start || ! insert[k] )
            output[j++] = source[i++];
        else
            output[j++] = insert[k++];
    }

    output[j] = '\0';

    for ( i = 0; output[i]; i++ )
        source[i] = output[i];

    source[i] = '\0';
}       /* -----  end of function insertString  ----- */
vxs8122
  • 834
  • 2
  • 10
  • 22
  • 2
    You cause a buffer overflow in all of youur examples. Sometimes your debugger detects it and sometimes it doesn't . – M.M Jul 03 '14 at 03:33
  • Why are you putting prototypes for the other functions in your first one? – Jonathon Reinhart Jul 03 '14 at 03:43
  • Because the first function uses all the last 3 functions. Is that bad thing? – vxs8122 Jul 03 '14 at 03:47
  • @vxs8122 -- Standard practice is to put function prototypes at the top of the file, after the `#include`s. That way, you don't have to worry if you rearrange things. Also, you don't have to constantly redeclare the functions everywhere you use them. – user3386109 Jul 03 '14 at 04:08

3 Answers3

3

When you do

char text[] = "a world";

you create an array of only 8 characters (the string and the terminating zero). When you replace the sub-string "a" with "hello" you will write beyond the end of the array, thereby "smashing" the stack.

Instead set a size of the array, making it big enough to fit the replacement, like

char text[16] = "a world";

Writing beyond the end of an array (or beyond the end of any allocated memory) leads to undefined behavior. And undefined behavior can be devious and hard to track down, because sometimes it might actually seem to work as expected.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

char text[] = "a world"; creates an array. Its length is determined by its initializer, which is 8 characters long (don't forget the null terminator!). You're replacing one character with five characters, for a total of 12. That's too long for the array, so you have an overrun. That smashes the stack.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
1

You are allocating a fixed-width string on the stack via char text[] = "a world";, where the compiler automatically determines the amount of space to allocate for this r/w piece of memory at build time, which is the length of the string plus room for a terminating NULL character, which amounts to 8 bytes in this particular example.

Your code will attempt to write outside the bounds of this section of memory, and will corrupt your stack by writing into areas of it which it shouldn't, also referred to as stack smashing.

You should take some time to read up on memory segmentation, noted in the references below. Also, a good habit to get into, if you insist on using static allocation of buffers rather than dynamic on the heap via malloc/calloc calls, would be to use a common constant via a macro, or a const size_t if your compiler is C99 compliant and supports variable length arrays (VLAs).

ie:

#define MAX_BUF_LENGTH (1024)
char text[MAX_BUF_LENGTH] = "a world";

You will use more space in memory, but can writer safer code via strlcat, strlcpy, etc, which allows you to safely guarantee the maximum number of bytes written, preventing such errors in the future. References


  1. Difference between declared string and allocated string, Accessed 2014-07-02, <https://stackoverflow.com/questions/16021454/difference-between-declared-string-and-allocated-string/16021546#16021546>
Community
  • 1
  • 1
Cloud
  • 18,753
  • 15
  • 79
  • 153