DF-0706 / ipfw3_sync_lifecycle_probe.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 | /* * DF-0706 โ ipfw3 sync lifecycle bugs reachability probe. * * The finding cites four lifecycle defects in ip_fw3_sync.c: * (a) UAF on replaced edge_sock (cached so vs global so) * (b) soclose+sofree refcount imbalance * (c) MOD_UNLOAD never joins edge_td (handler thread runs after module code unmapped) * (d) MOD_UNLOAD never clears ipfw_sync_send_state_prt (dangling fn ptr) * * All four require the IP_FW_SYNC_* opcodes to dispatch to * ip_fw3_ctl_sync_sockopt() โ which is gated by `ip_fw3_ctl_sync_ptr` * (sys/net/ipfw3/ip_fw3.c:133, init NULL). The pointer is never * assigned anywhere in the tree: * * $ grep -rn 'ip_fw3_ctl_sync_ptr =' sys/ * (no hits) * * The companion `ipfw_sync_send_state_prt` global is similarly never * reached: it would be set by `ip_fw3_sync_modevent(MOD_LOAD)` * (ip_fw3_sync.c:470-475), but `ip_fw3_sync_modevent` is **never * called by anyone** โ the ipfw3_basic module hook * (ip_fw3_basic.c:657-658) invokes only `ip_fw3_state_modevent` and * `ip_fw3_table_modevent`, skipping sync entirely. * * Conclusion: on a default kernel the entire ipfw3_sync.c code path is * dead. The lifecycle bugs are LATENT. We exercise every IP_FW_SYNC_* * opcode via IP_FW_X to confirm none of them reach the buggy code. * * Run as root (IP_FW_X needs a raw IP socket -> SYSCAP_NONET_RAW). */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #ifndef IP_FW_X #define IP_FW_X 49 #endif /* Opcodes from sys/net/ipfw3/ip_fw3.h */ #define OP_SYNC_SHOW_CONF 82 #define OP_SYNC_SHOW_STATUS 83 #define OP_SYNC_EDGE_CONF 84 #define OP_SYNC_EDGE_START 85 #define OP_SYNC_EDGE_STOP 86 #define OP_SYNC_EDGE_TEST 87 #define OP_SYNC_EDGE_CLEAR 88 #define OP_SYNC_CENTRE_CONF 89 #define OP_SYNC_CENTRE_START 90 #define OP_SYNC_CENTRE_STOP 91 #define OP_SYNC_CENTRE_TEST 92 #define OP_SYNC_CENTRE_CLEAR 93 /* ip_fw3_glue.c: ip_fw3_ctl_x() reads sopt_val as [xhdr][payload] * then memmoves payload over the xhdr in-place, sets sopt_name=opcode, * and calls ip_fw3_ctl(). */ struct ip_fw_x_header { uint16_t opcode; uint16_t _pad; } __attribute__((packed)); static int fire(int s, uint16_t opcode, const char *tag, void *payload, size_t plen) { size_t total = sizeof(struct ip_fw_x_header) + plen; char buf[512]; if (total > sizeof(buf)) return -ENOMEM; struct ip_fw_x_header *h = (struct ip_fw_x_header *)buf; h->opcode = opcode; h->_pad = 0; if (plen) memcpy(buf + sizeof(*h), payload, plen); int rc = setsockopt(s, IPPROTO_IP, IP_FW_X, buf, total); printf("[fire %-26s opcode=%u plen=%zu] rc=%d errno=%d (%s)\n", tag, opcode, plen, rc, errno, strerror(errno)); return rc; } int main(void) { int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)"); return 1; } printf("[*] running as: uid=%d euid=%d\n", getuid(), geteuid()); /* EDGE_CONF โ would socreate() edge_sock (bug a/b/c precondition) */ struct { int port; int hw_same; } ec = { 12345, 0 }; fire(s, OP_SYNC_EDGE_CONF, "IP_FW_SYNC_EDGE_CONF", &ec, sizeof(ec)); /* EDGE_START โ would soreference() + kthread_create edge_td */ int zero = 0; fire(s, OP_SYNC_EDGE_START, "IP_FW_SYNC_EDGE_START", &zero, sizeof(zero)); /* EDGE_STOP โ exercises the soclose+sofree imbalance path */ fire(s, OP_SYNC_EDGE_STOP, "IP_FW_SYNC_EDGE_STOP", &zero, sizeof(zero)); /* CENTRE_CONF / START โ would create centre_socks[] */ struct { int count; } cc = { 1 }; fire(s, OP_SYNC_CENTRE_CONF, "IP_FW_SYNC_CENTRE_CONF", &cc, sizeof(cc)); fire(s, OP_SYNC_CENTRE_START, "IP_FW_SYNC_CENTRE_START", &zero, sizeof(zero)); /* SHOW_STATUS โ reads fw3_sync_ctx.running (always 0 since sync never starts) */ int run = -1; fire(s, OP_SYNC_SHOW_STATUS, "IP_FW_SYNC_SHOW_STATUS", &run, sizeof(run)); /* SHOW_CONF โ read-back; would underflow on count if reached */ char buf[64] = {0}; fire(s, OP_SYNC_SHOW_CONF, "IP_FW_SYNC_SHOW_CONF", buf, sizeof(buf)); printf("[+] All opcodes returned without panic.\n"); printf("[+] ip_fw3_ctl_sync_ptr is NULL on master => dispatcher falls through.\n"); printf("[+] MOD_UNLOAD of ipfw3_basic never invokes ip_fw3_sync_modevent()\n"); printf(" so ipfw_sync_send_state_prt is never set and the edge_td thread\n"); printf(" is never created โ the cited lifecycle bugs cannot fire.\n"); close(s); return 0; } |