1

The second call to strcat here is generating a segmentation fault, why?

#include <unistd.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>  
#include <pthread.h>

int main (int argc, char * argv[])
{
         char command[250];

         //if(scanf("%199s", command) == 1)

            gets(command);
            puts(command);

         int pipeIntId; 

         char whitespaceseparator[2]=" ";

         char pidstring [30];

         int pid= getpid(); 

         sprintf(pidstring,"%d", pid);

         char * whitespace_and_pid;

         whitespace_and_pid = strcat(whitespaceseparator,pidstring);  


         char * command_and_pid; 

         command_and_pid=strcat(command,whitespace_and_pid); // here's the problem, I guess


          if((mkfifo("pipe"/*pipeName*/,0666))==-1) 
          {
              perror("error creating pipe 1");
          exit(1);
          }

         if((pipeIntId=open("pipe",/*pipeName*/O_WRONLY))==-1)
         {
          perror("error creating pipe 2");
          exit(1);
         }


         int written;

         written=write(pipeIntId,command_and_pid,250); // send the command + the pid


         close(pipeIntId);

    return 0;
}
andandandand
  • 21,946
  • 60
  • 170
  • 271

7 Answers7

4

I tried your code, and also see the segfault on the second strcat(). I found that command[250] is allocated immediately after whitespaceseparator[2] on the stack on my system:

(gdb) p &whitespaceseparator 
$1 = (char (*)[2]) 0xbf90acd4
(gdb) p &command
$2 = (char (*)[250]) 0xbf90acd6

e.g. (here command begins "foo..."), things are layed out like this:

 whitespaceseparator
  |
  |      command
  |       |
  v       v
+---+---+---+---+---+---+---+---+
|' '| 0 |'f'|'o'|'o'|'.'|'.'|'.'| ...
+---+---+---+---+---+---+---+---+

I can't guarantee that the same happens on your system (layout of locals on the stack may vary even between different versions of the same compiler), but it seems likely. On mine, here is exactly what happens:

As others have said, strcat() appends the second string to the first (and the result will be equal to the first argument). So, the first strcat() overflows whitespaceseparator[] (and returns whitespaceseparator as whitespace_and_pid):

+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'| 0 |'.'|'.'| ...
+---+---+---+---+---+---+---+---+

The second strcat() tries to append whitespace_and_pid (== whitespaceseparator) to the string at command. The first character of the copy will overwrite the terminating 0 of the string at command:

  |    ===copy===>    |
  v                   v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'.'|'.'| ...
+---+---+---+---+---+---+---+---+

The copy continues...

      |    ===copy===>    |
      v                   v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'1'|'.'| ...
+---+---+---+---+---+---+---+---+

          |    ===copy===>    |
          v                   v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'1'|'2'| ...
+---+---+---+---+---+---+---+---+

and will carry on copying " 1234 1234 1234"... until it falls off the end of the process address space, at which point you get a segfault.

Matthew Slattery
  • 45,290
  • 8
  • 103
  • 119
3

strcat doesn't do what you think. It modifies the string pointed to by its first parameter. In this case, that string is contained in a 2-byte array, which is therefore overrun.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Oh, I got that from here http://www.thinkage.ca/english/gcos/expl/c/lib/strcat.html. I'm curious about why it does work the first time it's run. – andandandand Nov 30 '10 at 01:19
  • I don't think it does "work" the first time it's run: copying `pidstring` to the end of `whitespaceseparator` is undefined behavior, that doesn't mean it necessarily crashes straight away. Matthew's answer has a very plausible explanation why you would see a crash on the line you indicate, assuming your implementation does the same as his. – Steve Jessop Nov 30 '10 at 02:26
  • No, I don't mean it works using this case of pidstring with whitespaceseparator. It works when used the first time it's run with a buffer of sufficient size, changing the size of whitespaceseparator in this case to a big value won't make it work either. – andandandand Nov 30 '10 at 03:29
2

To avoid buffer overflow errors, but use strcat you should to use strncat function.

Eir Nym
  • 1,515
  • 19
  • 30
1

Your gets call could have added sufficient characters already to cause undefined behavior at about anytime.

user502515
  • 4,346
  • 24
  • 20
1

whitespaceseparator isn't big enough to contain the concatenated string, so you're causing undefined behaviour.

Using gets is normally frowned upon, too.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
1

strcat is generally unsafe because it can happily overrun buffers, like it does in your case.

First of all, whitespaceseparator is only two bytes large? Are you sure that's what you want? And you concatenate pidstring to it? I think you got the arguments mixed up.

In general though, strcat will cause hard-to-debug crashes if you're not very careful with your buffer sizes. There are safer alternatives.

EboMike
  • 76,846
  • 14
  • 164
  • 167
1

"String concatenation" is an idiom you should drop when learning C. Not only does it lead to a lot of bugs with overflowing buffers; it's also super inefficient. In your code, you could just have included the space in the snprintf format string (you should be using it in place of sprintf).

Whenever possible, try to assemble a string entirely in one step using snprintf. This consolidates all of the buffer length checking into one place and makes it really hard to get wrong. You can also call snprintf with a 0 size argument to get the length that the combined string would be, in order to find out what size to allocate, if the size of the output is not known in advance (you should allocate one more byte than this length so that the null terminator does not truncate your output).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711