• Russ Cox's avatar
    runtime, syscall: work around FreeBSD/amd64 kernel bug · 555da73c
    Russ Cox authored
    The kernel implementation of the fast system call path,
    the one invoked by the SYSCALL instruction, is broken for
    restarting system calls. A C program demonstrating this is below.
    
    Change the system calls to use INT $0x80 instead, because
    that (perhaps slightly slower) system call path actually works.
    
    I filed http://www.freebsd.org/cgi/query-pr.cgi?pr=182161.
    
    The C program demonstrating that it is FreeBSD's fault is below.
    It reports the same "Bad address" failures from wait.
    
    #include <sys/time.h>
    #include <sys/signal.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    static void handler(int);
    static void* looper(void*);
    
    int
    main(void)
    {
            int i;
            struct sigaction sa;
            pthread_cond_t cond;
            pthread_mutex_t mu;
    
            memset(&sa, 0, sizeof sa);
            sa.sa_handler = handler;
            sa.sa_flags = SA_RESTART;
            memset(&sa.sa_mask, 0xff, sizeof sa.sa_mask);
            sigaction(SIGCHLD, &sa, 0);
    
            for(i=0; i<2; i++)
                    pthread_create(0, 0, looper, 0);
    
            pthread_mutex_init(&mu, 0);
            pthread_mutex_lock(&mu);
            pthread_cond_init(&cond, 0);
            for(;;)
                    pthread_cond_wait(&cond, &mu);
    
            return 0;
    }
    
    static void
    handler(int sig)
    {
    }
    
    int
    mywait4(int pid, int *stat, int options, struct rusage *rusage)
    {
            int result;
    
            asm("movq %%rcx, %%r10; syscall"
                    : "=a" (result)
                    : "a" (7),
                      "D" (pid),
                      "S" (stat),
                      "d" (options),
                      "c" (rusage));
    }
    
    static void*
    looper(void *v)
    {
            int pid, stat, out;
            struct rusage rusage;
    
            for(;;) {
                    if((pid = fork()) == 0)
                            _exit(0);
                    out = mywait4(pid, &stat, 0, &rusage);
                    if(out != pid) {
                            printf("wait4 returned %d\n", out);
                    }
            }
    }
    
    Fixes #6372.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/13582047
    555da73c
asm_freebsd_amd64.s 2.9 KB