DF-3154 / harness.c
/* * DF-3154 deterministic harness โ transcribes ext2_ei_csum_verify()'s * zero-inode test (sys/vfs/ext2fs/ext2_csum.c:583-590) verbatim: * * static const struct ext2fs_dinode ei_zero; // 160 bytes * ... * if (provided != calculated) { * if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) * return (0); * * sizeof(struct ext2fs_dinode) == 160 here (fields end at e2di_projid+4), * but the on-disk slot is EXT2_INODE_SIZE(fs) == 128 (mount-legal, * ext2_vfsops.c:539-545) and ext2_vget() hands ei = bp->b_data + * 128*ino_to_fsbo(ino) of a 1024-byte bread() buffer (ext2_vfsops.c:1372). * * Case A (slot idx 6): memcmp spans buffer bytes [768,928) โ the last * 32 bytes belong to the NEXT inode's slot (cross-inode read). * Case B (slot idx 7, last-in-block): bytes [1024,1056) lie 32 bytes * PAST the buffer -> kernel-heap read; against a guard page: SIGSEGV. */ #include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define DINO_SIZE 160 /* sizeof(struct ext2fs_dinode), ext2_dinode.h */ #define ISIZE 128 /* EXT2_INODE_SIZE(fs), mount-legal */ #define BSIZE 1024 /* e2fs_bsize */ #define IPB (BSIZE / ISIZE) static char zero160[DINO_SIZE]; /* ei_zero */ static char * guard_buf(size_t ps) { char *two = mmap(NULL, ps * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (two == MAP_FAILED) { perror("mmap"); exit(1); } if (mprotect(two + ps, ps, PROT_NONE)) { perror("mprotect"); exit(1); } return (two + ps - BSIZE); /* 1024-byte buffer flush to guard */ } int main(void) { size_t ps = (size_t)sysconf(_SC_PAGESIZE); char *buf = guard_buf(ps); int r, st; pid_t pid; printf("sizeof(struct ext2fs_dinode)=%d EXT2_INODE_SIZE(fs)=%d " "bsize=%d ipb=%d\n", DINO_SIZE, ISIZE, BSIZE, IPB); printf("slot idx %d: memcmp range [%d,%d) -> %d bytes into NEXT slot\n", IPB - 2, (IPB - 2) * ISIZE, (IPB - 2) * ISIZE + DINO_SIZE, (IPB - 2) * ISIZE + DINO_SIZE - (IPB - 1) * ISIZE); printf("slot idx %d: memcmp range [%d,%d) -> %d bytes PAST buffer\n", IPB - 1, (IPB - 1) * ISIZE, (IPB - 1) * ISIZE + DINO_SIZE, (IPB - 1) * ISIZE + DINO_SIZE - BSIZE); /* ---- Case A: cross-slot read (in-buffer, deterministic) ---- */ memset(buf, 0, BSIZE); memset(buf + (IPB - 1) * ISIZE, 0xAA, 32); /* next slot head */ r = memcmp(buf + (IPB - 2) * ISIZE, zero160, DINO_SIZE); printf("case A (next slot = 0xAA..): memcmp=%d -> EIO path\n", r); memset(buf + (IPB - 1) * ISIZE, 0x00, 32); r = memcmp(buf + (IPB - 2) * ISIZE, zero160, DINO_SIZE); printf("case A (next slot zeroed): memcmp=%d -> csum error suppressed\n", r); /* ---- Case B: read past the buffer (guard page) ---- */ printf("case B (last slot, guard page after buffer): " "fork+memcmp(slot %d, zero, %d) ...\n", IPB - 1, DINO_SIZE); fflush(stdout); pid = fork(); if (pid == 0) { r = memcmp(buf + (IPB - 1) * ISIZE, zero160, DINO_SIZE); printf("case B: UNEXPECTED no-fault (memcmp=%d)\n", r); _exit(2); } waitpid(pid, &st, 0); if (WIFSIGNALED(st)) printf("case B: child SIGSEGV'd as predicted " "(signal %d) โ the %d-byte over-read crossed the buffer end\n", WTERMSIG(st), (IPB - 1) * ISIZE + DINO_SIZE - BSIZE); else printf("case B: child exited %d\n", WEXITSTATUS(st)); return (0); } |