DF-2611 / kmem_addrlen.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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | /* * kmem_addrlen.c -- DF-2611 harness helper. * * Walks the kernel's ifnet_array (sys/net/if_var.h:736-740) through * /dev/kmem to find an interface by name, VALIDATES the computed * offsetof(struct ifnet, if_data) (+136) by printing ifi_type / * ifi_addrlen / ifi_mtu for every interface (vtnet0 must read * type=6/ETH, addrlen=6, mtu=1500; lo0 type=24/LOOP, addrlen=0, * mtu=16384), and optionally rewrites ifi_addrlen (offset 138). * * This simulates, on the QEMU guest, an interface whose link-layer * address length is not 6 (e.g. FireWire EUI-64, if_addrlen=8), which * the finding requires for the TLLA pad leak but which QEMU cannot * emulate with real hardware. * * usage: kmem_addrlen [ifname [new_addrlen]] * no args : dump all interfaces, validate offsets, change nothing * 1 arg : dump + show target * 2 args : dump + write new_addrlen into target's ifi_addrlen */ #include <sys/types.h> #include <sys/elf_common.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <stdint.h> #include <errno.h> #define IFDATA_OFF 136 /* offsetof(struct ifnet, if_data) */ #define ADDRLEN_OFF (IFDATA_OFF + 2) /* ifi_addrlen */ #define TYPE_OFF (IFDATA_OFF + 0) /* ifi_type */ #define MTU_OFF (IFDATA_OFF + 8) /* ifi_mtu (u_long) */ static int kmem = -1; static int kread(uint64_t addr, void *buf, size_t len) { if (lseek(kmem, (off_t)addr, SEEK_SET) == (off_t)-1 || read(kmem, buf, len) != (ssize_t)len) return -1; return 0; } static int kwrite(uint64_t addr, const void *buf, size_t len) { if (lseek(kmem, (off_t)addr, SEEK_SET) == (off_t)-1 || write(kmem, buf, len) != (ssize_t)len) return -1; return 0; } /* find symbol address via nm from the booted kernel image */ static uint64_t symaddr(const char *name) { char cmd[256]; FILE *p; char line[512], sym[256]; uint64_t addr; int n; snprintf(cmd, sizeof(cmd), "nm -n /boot/kernel/kernel 2>/dev/null"); p = popen(cmd, "r"); if (!p) { perror("popen nm"); return 0; } addr = 0; while (fgets(line, sizeof(line), p)) { n = sscanf(line, "%llx %*s %255s", &addr, sym); if (n == 2 && !strcmp(sym, name)) break; addr = 0; } pclose(p); return addr; } static const char * iftname(int t) { switch (t) { case 6: return "IFT_ETHER(6)"; case 24: return "IFT_LOOP(24)"; case 54: return "IFT_PROPVIRTUAL(54)"; case 18: return "IFT_P2P_SERIAL? (18)"; default: return "?"; } } int main(int argc, char **argv) { uint64_t arr_p, ifp, ifnet_arr_addr; uint8_t buf[512]; int count, i, found = 0, want_write = 0; uint8_t newlen = 0; const char *target = NULL; char name[17]; if (argc > 2) { want_write = 1; newlen = (uint8_t)strtoul(argv[2], NULL, 0); } if (argc > 1) target = argv[1]; kmem = open("/dev/kmem", O_RDWR); if (kmem < 0) { perror("open /dev/kmem"); return 2; } ifnet_arr_addr = symaddr("ifnet_array"); if (!ifnet_arr_addr) { fprintf(stderr, "cannot resolve ifnet_array symbol\n"); return 2; } printf("ifnet_array symbol @ 0x%llx\n", (unsigned long long)ifnet_arr_addr); if (kread(ifnet_arr_addr, &arr_p, sizeof(arr_p)) || !arr_p) { fprintf(stderr, "cannot read ifnet_array pointer\n"); return 2; } if (kread(arr_p, buf, 8)) { fprintf(stderr, "cannot read ifnet_array {count,pad}\n"); return 2; } count = *(int *)buf; printf("ifnet_array @ 0x%llx count=%d\n", (unsigned long long)arr_p, count); if (count < 1 || count > 1024) { fprintf(stderr, "implausible count -- offsets wrong?\n"); return 2; } for (i = 0; i < count; i++) { uint8_t *ent; uint64_t e; int ftype; uint8_t flen; unsigned long fmtu; if (kread(arr_p + 8 + (uint64_t)i * 8, &e, sizeof(e)) || !e) continue; if (kread(e, buf, sizeof(buf))) continue; ent = buf; memcpy(name, ent + 32, 16); /* if_xname @ +32 */ name[16] = 0; ftype = ent[TYPE_OFF]; flen = ent[ADDRLEN_OFF]; memcpy(&fmtu, ent + MTU_OFF, sizeof(fmtu)); printf("ifnet[%d] 0x%llx %-8s type=%d(%s) addrlen=%u " "mtu=%lu\n", i, (unsigned long long)e, name, ftype, iftname(ftype), flen, fmtu); if (target && !strcmp(name, target)) { found = 1; if (want_write) { if (kwrite(e + ADDRLEN_OFF, &newlen, 1)) { fprintf(stderr, "kwrite failed: %s\n", strerror(errno)); return 2; } if (kread(e + ADDRLEN_OFF, &flen, 1)) return 2; printf("WROTE %s if_addrlen=%u (readback)\n", target, flen); } } } if (target && !found) { fprintf(stderr, "interface %s not found\n", target); return 1; } close(kmem); return 0; } |