DF-0035 / dump_msgbuf.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 | /* Dump msgbuf internals via kvm(3) for DF-0035 verification. * Needs /dev/kmem read access (root). Reports msg_magic/size/bufx/bufr/bufl * and computes the sysctl_kern_msgbuf branch decision so we can see whether * branch 3 (the buggy one) is even reachable in the current state. */ #include <sys/types.h> #include <sys/msgbuf.h> #include <sys/param.h> #include <fcntl.h> #include <kvm.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <nlist.h> static struct nlist nl[] = { { .n_name = "msgbufp" }, { .n_name = NULL } }; int main(int argc, char **argv) { kvm_t *kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, "kvm_open"); if (!kd) { fprintf(stderr, "kvm_openfiles failed\n"); return 1; } if (kvm_nlist(kd, nl) != 0 || nl[0].n_value == 0) { fprintf(stderr, "nlist msgbufp failed\n"); kvm_close(kd); return 1; } /* msgbufp is `struct msgbuf *`; read the pointer first. */ struct msgbuf *mbp_ptr; if (kvm_read(kd, nl[0].n_value, &mbp_ptr, sizeof(mbp_ptr)) != sizeof(mbp_ptr)) { fprintf(stderr, "kvm_read pointer failed: %s\n", kvm_geterr(kd)); kvm_close(kd); return 1; } printf("msgbufp (kernel pointer) = %p\n", (void *)mbp_ptr); struct msgbuf mb; if (kvm_read(kd, (unsigned long)mbp_ptr, &mb, sizeof(mb)) != sizeof(mb)) { fprintf(stderr, "kvm_read msgbuf failed: %s\n", kvm_geterr(kd)); kvm_close(kd); return 1; } printf("msg_magic = 0x%08x (expect 0x%08x)\n", mb.msg_magic, (unsigned)MSG_MAGIC); printf("msg_size = %u\n", mb.msg_size); printf("msg_ptr = %p\n", (void *)mb.msg_ptr); printf("msg_bufx = %u (mod size = %u)\n", mb.msg_bufx, mb.msg_bufx % mb.msg_size); printf("msg_bufr = %u (mod size = %u)\n", mb.msg_bufr, mb.msg_bufr % mb.msg_size); printf("msg_bufl = %u (mod size = %u)\n", mb.msg_bufl, mb.msg_bufl % mb.msg_size); /* Simulate sysctl_kern_msgbuf's branch decision. */ u_int rindex = mb.msg_bufr; u_int xindex = mb.msg_bufx; u_int n = xindex - rindex; int shrank = 0; if (n > mb.msg_size - 1024) { rindex = xindex - mb.msg_size + 2048; n = xindex - rindex; shrank = 1; } u_int rindex_modulo = rindex % mb.msg_size; u_int xindex_modulo = xindex % mb.msg_size; int branch; u_int out_off, out_len_buggy, out_len_correct; if (rindex_modulo < xindex_modulo) { branch = 1; out_off = rindex_modulo; out_len_buggy = out_len_correct = xindex_modulo - rindex_modulo; } else if (rindex_modulo == xindex_modulo) { branch = 2; out_off = 0; out_len_buggy = out_len_correct = 1; /* the "\n" */ } else if (n <= mb.msg_size - rindex_modulo) { branch = 3; out_off = rindex_modulo; out_len_correct = n; out_len_buggy = n - rindex_modulo; /* THE BUG */ } else { branch = 4; out_off = rindex_modulo; out_len_correct = n; out_len_buggy = mb.msg_size - rindex_modulo; /* first half; second half uses n */ } printf("\n-- sysctl_kern_msgbuf decision --\n"); printf("shrank = %d\n", shrank); printf("n = %u\n", n); printf("rindex_mod = %u xindex_mod = %u\n", rindex_modulo, xindex_modulo); printf("BRANCH = %d\n", branch); printf("out_off = %u (from msg_ptr)\n", out_off); printf("out_len(correct) = %u\n", out_len_correct); if (branch == 3) { printf("out_len(buggy) = %u (n - rindex_modulo)\n", out_len_buggy); if (rindex_modulo > n) { printf(">>> UNDERFLOW! rindex_modulo(%u) > n(%u) -- OOB read of ~%u bytes past msg_ptr+msg_size <<<\n", rindex_modulo, n, out_len_buggy); } else { printf("rindex_modulo(%u) <= n(%u): no underflow (just a %d-byte under-read)\n", rindex_modulo, n, (int)(out_len_correct - out_len_buggy)); } } kvm_close(kd); return 0; } |