Search code examples
cmultithreadingcatfifotee

Print and read from xterm in a multithreaded C program using cat, tee and fifos


I am trying to create a chat interface in my C program. The program use two fifos to communicate with a forked xterm running the commands cat fifo_out and tee fifo_in > /dev/null at the same time. Then a thread is opened for reading input in the *fifo_in coming from the terminal thanks to tee, in the same time another thread prints message in fifo_out and things get displayed in xterm.

All seems to work pretty well... Except when a message has been printed while writing some text in the terminal, splitting the entered text in two parts. This cause a segfault!

Do you have any idea why it happens like that ?

Here is a minimal example:

int open_terminal(pid_t *pid, int *pipe)
{
  mkfifo("fifo_in", 0600);
  mkfifo("fifo_out", 0600); 
  pid_t p = fork();
  int fd_in, fd_out;
  switch (p) {
    case -1:
      return -1;
    case 0:
        execlp("xterm", "xterm", "-e", "cat fifo_out & tee fifo_in > /dev/null", NULL);
        exit(EXIT_FAILURE);
        break;
    default:
        if ((fd_in = open("fifo_in", O_RDONLY)) == - 1)
          return -1;
        if ((fd_out = open("fifo_out", O_WRONLY)) == - 1)
          return -1;
        *pid = p;
        pipe[0] = fd_in; pipe[1] = fd_out;
        return 0;
  }
  return -1;
}


void *message_thread(void *args)
{
  int *fd_out = (int *)args;
  while (1) {
    dprintf(*fd_out, "You're awesome!\n");
    sleep(5);
  }
}


void *input_thread(void *args)
{
  int *fd_in = (int *)args;
  FILE *f = fdopen(*fd_in, "r");
  while (1) {
    size_t n;
    char *line;
    getline(&line, &n, f);
    printf("Read: %s", line);
    free(line);
    if(strcmp(line, "exit\n") == 0)
      return NULL;
  }
}


int main(int argc, char *argv[])
{
  pid_t pid;
  int pipe[2];
  if (open_terminal(&pid, pipe) == -1) {
    printf("Can't open terminal.\n");
    return 1;
  }
  pthread_t mt, it;
  pthread_create(&mt, NULL, message_thread, &pipe[1]);
  pthread_create(&it, NULL, input_thread, &pipe[0]);

  pthread_join(it, NULL);

  return 0;
}

To reproduce my conditions, run the program input some text, wait until some text is printed after your text then type again and enter.


Solution

  • From the input_thread function:

    free(line);
    if(strcmp(line, "exit\n") == 0)
    

    First you free the memory allocated and pointed to by line, then you immediately use that memory. This leads to undefined behavior.

    And that's not all, because you pass an uninitialized pointer and size to the getline function, it will not actually allocate memory since uninitialized local variables have an indeterminate value (it will be seemingly random and not very likely zero or NULL). Explicitly initialize the variables to zero and NULL, and the getline function will allocate the memory needed. Using uninitialized local (non-static) variables for anything else but initialization also leads to undefined behavior.

    It's probably the undefined behavior from getline writing to some seemingly random memory that causes the crash.