csystem-callsstack-smash

Why do I get a "stack smashing detected" error unless I store the file descriptors?


I'm playing around with a few I/O system calls: Read 2 bytes from an existing file, place them in a buffer, and then write those bytes to another empty, existing file.

// read_write.c
int open(char *filename, int access);
long read(int fd, void *buffer, unsigned long byte_count);
long write(int fd, void *buffer, unsigned long byte_count);
int close(int fd);

// assume both `source.txt` and `target.txt` exist.
int main(void) {
    char buffer;

    // open for reading only (fd = 3).
    open("source.txt", 0); 

    // open for writing only (fd = 4).
    open("target.txt", 1); 

    // read 2 bytes from fd = 3 to buffer.
    read(3, &buffer, 2);

    // reads 2 bytes from buffer to fd = 4.
    write(4, &buffer, 2);

    // close both file descriptors 3 and 4.
    close(3);
    close(4);

    return 0;
}

When I compile and run the above program, I get the following error:

$ gcc read_write.c && ./a.out 
*** stack smashing detected ***: terminated
Aborted (core dumped)
$ cat target.txt 
ab
$ 

As you can see the program does the read and write from one file to another but I'm trying to figure out what the case could be for that error. There's this SO explanation about stack smashing detected and it says that it usually signals a buffer overflow errors, but I don't see how storing the file descriptors in variables would do away with such errors.

When I store both file descriptors (e.g., int sfd = open("source.txt", 0);), and despite not using them anywhere else in the program, it works without errors.


Solution

  • Seemingly correct result is one way of undefined behavior.

    As written in a comment, you see undefined behavior caused by out-of-bounds access to buffer in read(3, &buffer, 2);.

    If this overwrites some pattern used by the stack protection code generated by the compiler, it results in the error you get.

    int sfd = ...; allocates another variable on the stack which expands the usable stack memory. I guess that it happens to use the memory after buffer. In this situation the variable sfd or a possible padding byte between the variables will get overwritten by your out-of-bounds access, keeping the stack-protection pattern intact.