Hello.

I manually simplified the reproducer. Since the bug is timing dependent,
this reproducer might fail to reproducer the bug. Anyway, I guess that
there is a race condition between

        vfree(ldata);
        tty->disc_data = NULL;

at n_tty_close() by something (ioctl(TIOCVHANGUP) ?) and

        struct n_tty_data *ldata = tty->disc_data;

        if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) 
{
                bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);

at n_tty_set_termios() by ioctl(TCSETS), for the report says that ldata == NULL
("Write of size 512 at addr 0000000000001060").

----------------------------------------
#include <fcntl.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <termios.h>
#define TIOCGPTPEER     _IO('T', 0x41) 

static const int zero = 0;
static int master_fd = EOF;
static int slave_fd = EOF;
static void execute_call(int call)
{
        struct termios term;
        char buf[128];
        int ptyno = 0;
        switch (call) {
        case 0:
                master_fd = open("/dev/ptmx", O_RDONLY);
                ioctl(master_fd, TIOCSPTLCK, &zero);
                if (ioctl(master_fd, TIOCGPTN, &ptyno))
                        break;
                sprintf(buf, "/dev/pts/%d", ptyno);
                slave_fd = open(buf, O_RDONLY, 0);
                usleep(5 * 1000);
                ioctl(slave_fd, TIOCVHANGUP, 0);
                break;
        case 5:
                memset(&term, 0, sizeof(term));
                ioctl(master_fd, TCSETS, &term);
                break;
        case 6:
                ioctl(master_fd, TIOCGPTPEER, 0);
                break;
        }
}

struct thread_t {
        int created, running, call;
        pthread_t th;
};

static struct thread_t threads[16];
static int running;
static int collide;

static void* thr(void* arg)
{
        struct thread_t* th = (struct thread_t*)arg;
        for (;;) {
                while (!__atomic_load_n(&th->running, __ATOMIC_ACQUIRE))
                        syscall(SYS_futex, &th->running, FUTEX_WAIT, 0, 0);
                execute_call(th->call);
                __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED);
                __atomic_store_n(&th->running, 0, __ATOMIC_RELEASE);
                syscall(SYS_futex, &th->running, FUTEX_WAKE);
        }
        return 0;
}

static void execute(int num_calls)
{
        int call, thread;
        running = 0;
        for (call = 0; call < num_calls; call++) {
                for (thread = 0; thread < sizeof(threads) / sizeof(threads[0]); 
thread++) {
                        struct thread_t* th = &threads[thread];
                        if (!th->created) {
                                th->created = 1;
                                pthread_attr_t attr;
                                pthread_attr_init(&attr);
                                pthread_attr_setstacksize(&attr, 128 << 10);
                                pthread_create(&th->th, &attr, thr, th);
                        }
                        if (!__atomic_load_n(&th->running, __ATOMIC_ACQUIRE)) {
                                th->call = call;
                                __atomic_fetch_add(&running, 1, 
__ATOMIC_RELAXED);
                                __atomic_store_n(&th->running, 1, 
__ATOMIC_RELEASE);
                                syscall(SYS_futex, &th->running, FUTEX_WAKE);
                                if (collide && call % 2)
                                        break;
                                struct timespec ts;
                                ts.tv_sec = 0;
                                ts.tv_nsec = 20 * 1000 * 1000;
                                syscall(SYS_futex, &th->running, FUTEX_WAIT, 1, 
&ts);
                                if (running)
                                        usleep((call == num_calls - 1) ? 10000 
: 1000);
                                break;
                        }
                }
        }
}

int main(int argc, char *argv[])
{
        while (1) {
                execute(7);
                collide = 1;
                execute(7);
        }
        return 0;
}
----------------------------------------

Reply via email to