DragonFlyBSD Kernel Audit
DF-0704 / ipfw3_sync_probe.c
← back to finding ↓ download raw
/*
 * DF-0704 / DF-0705 reachability probe.
 *
 * Both findings cite panics in sys/net/ipfw3_basic/ip_fw3_sync.c. To reach
 * them, the IP_FW_SYNC_* opcodes (set via IP_FW_X) must dispatch to
 * ip_fw3_ctl_sync_sockopt(). That dispatch is gated by the global function
 * pointer `ip_fw3_ctl_sync_ptr` (sys/net/ipfw3/ip_fw3.c:133, init NULL).
 *
 *   1125:        if (ip_fw3_ctl_sync_ptr != NULL) {
 *   1126:            error = ip_fw3_ctl_sync_ptr(sopt);   <- only call site
 *
 * grep shows `ip_fw3_ctl_sync_ptr = ` NEVER appears anywhere in the tree.
 * The only initialiser for the related sync state, ip_fw3_sync_modevent()
 * (ip_fw3_sync.c:470), is also never invoked — ipfw3_basic's module hook
 * (ip_fw3_basic.c:657-658) calls only state_modevent and table_modevent.
 *
 * Conclusion: on a default kernel, the entire ipfw3_sync.c code path is dead.
 * This probe issues IP_FW_SYNC_EDGE_START / IP_FW_SYNC_CENTRE_CONF (negative
 * count) / IP_FW_SYNC_SHOW_CONF and confirms each returns success-with-no-
 * effect (the opcode silently falls through), proving the buggy functions
 * are unreachable.
 *
 * Run as root (IP_FW_X requires a raw IP socket, which requires
 * SYSCAP_NONET_RAW). An unprivileged user cannot even create the socket.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.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_EDGE_START   85
#define OP_SYNC_CENTRE_CONF  89
#define OP_SYNC_SHOW_CONF    82

struct ip_fw_x_header {
    uint16_t opcode;
    uint16_t _pad;
} __attribute__((packed));

static int
fire(int s, uint16_t opcode, void *payload, size_t plen)
{
    /* the glue layer: 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(). */
    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 opcode=%u plen=%zu] setsockopt rc=%d errno=%d (%s)\n",
           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("[*] invoking IP_FW_SYNC_EDGE_START (DF-0705 panic 1: NULL edge_sock -> sobind)\n");
    fire(s, OP_SYNC_EDGE_START, NULL, 0);

    printf("[*] invoking IP_FW_SYNC_CENTRE_CONF with count=-1 (DF-0705 panic 3: huge bcopy)\n");
    struct {
        int count;
        /* edges[0] is flexible; none needed for count=-1 */
    } cc = { .count = -1 };
    fire(s, OP_SYNC_CENTRE_CONF, &cc, sizeof(cc));

    printf("[*] invoking IP_FW_SYNC_SHOW_CONF (DF-0705 panic 3 read-back path)\n");
    char out[64];
    memset(out, 0, sizeof(out));
    fire(s, OP_SYNC_SHOW_CONF, out, sizeof(out));

    printf("[+] All three returned without panic.\n");
    printf("[+] Conclusion: ip_fw3_ctl_sync_ptr is NULL; IP_FW_SYNC_* opcodes are dead code.\n");
    close(s);
    return 0;
}