DF-2837 / sbmax.c
/* * DF-2837 PoC: sysctl_handle_sb_max() (uipc_socket2.c:686-704) maintains * the `u_long sb_max` through sizeof(int) (4-byte) SYSCTL_IN/SYSCTL_OUT * operations even though the variable is 8 bytes on amd64: * * - an 8-byte write is silently accepted and only its LOW 4 bytes are * stored (the upper half is ignored, no error); * - consequently sb_max can never hold a value >= 4 GiB, and any * attempt wraps to the truncated magnitude silently. * * Root-only writer; this demonstrates the type confusion, not an * unprivileged attack. */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(void) { int oldv = 0, nowv = 0; size_t len = sizeof(oldv); u_long v8; int error; error = sysctlbyname("kern.ipc.maxsockbuf", &oldv, &len, NULL, 0); printf("current kern.ipc.maxsockbuf = %d (sysctl ok=%d)\n", oldv, error == 0); /* 8-byte write whose meaningful value is 0x1_00001000 (4 GiB + 4 KiB) */ v8 = 0x0000000100001000UL; error = sysctlbyname("kern.ipc.maxsockbuf", NULL, NULL, &v8, sizeof(v8)); printf("write #1: 8-byte value 0x%016llx -> sysctl returned %d (%s)\n", (unsigned long long)v8, error, error ? strerror(errno) : "ok"); len = sizeof(nowv); sysctlbyname("kern.ipc.maxsockbuf", &nowv, &len, NULL, 0); printf("after 8-byte write of 0x100001000, sb_max = %d " "(0x%x) <-- low 32 bits kept, high half silently dropped\n", nowv, nowv); /* 8-byte write that is exactly 4 GiB + 524288 */ v8 = 0x0000000100080000UL; error = sysctlbyname("kern.ipc.maxsockbuf", NULL, NULL, &v8, sizeof(v8)); len = sizeof(nowv); sysctlbyname("kern.ipc.maxsockbuf", &nowv, &len, NULL, 0); printf("write #2: 8-byte 0x100080000 -> ret=%d, sb_max = %d " "(0x%x) <-- became 0x80000, magnitude wrong by 4 GiB\n", error, nowv, nowv); /* restore */ v8 = (u_long)oldv; error = sysctlbyname("kern.ipc.maxsockbuf", NULL, NULL, &v8, sizeof(v8)); len = sizeof(nowv); sysctlbyname("kern.ipc.maxsockbuf", &nowv, &len, NULL, 0); printf("restored: sb_max = %d (ret=%d)\n", nowv, error); return (0); } |