DF-4714 / trigger.c
/* * DF-4714 trigger: ONE 32KB write() spanning both data blocks of a * RAID-5 stripe (volume sectors [128,192), stripesize 32s, 3 sds: * parity ordinal 0, data ordinals 1 and 2). * * Block 1 pattern: 0xAA (volume sectors [128,160) -> data sd s1) * Block 2 pattern: 0x55 (volume sectors [160,192) -> data sd s2) * * With s1+s2 both in a "usable-for-write-but-not-up" state (sd_reborn, * e.g. after their shared drive flaps down->up), bre5()'s second-bad- * subdisk guard at vinumraid5.c:404 only fires when checksdstate() * returns non-zero. checksdstate(sd_reborn, WRITE) returns REQUEST_OK, * so m.badsdno is silently OVERWRITTEN (line 420) and the request is * built as a single-subdisk degraded write: s1's block is read (old * data), only s2's NEW user data is XORed into parity, neither data * subdisk is written, and the write() returns SUCCESS. * * Expected (buggy kernel): write OK, readback returns stale bytes. * Expected (fixed kernel): write fails EIO (two bad data columns). */ #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #define VOL "/dev/vinum/vol/rv" #define OFF (128 * 512) /* sector 128 */ #define LEN (64 * 512) /* 64 sectors = 2 blocks */ unsigned char wbuf[LEN], rbuf[LEN]; int main(void) { int fd, rc, i, bad; for (i = 0; i < 32 * 512; i++) wbuf[i] = 0xAA; /* block 1 */ for (i = 32 * 512; i < LEN; i++) wbuf[i] = 0x55; /* block 2 */ memset(rbuf, 0, LEN); fd = open(VOL, O_RDWR); if (fd < 0) { perror("open"); return 2; } rc = lseek(fd, OFF, SEEK_SET); if (rc < 0) { perror("lseek"); return 2; } rc = write(fd, wbuf, LEN); if (rc != LEN) { perror("write"); printf("RESULT: write FAILED (%d) - correct for a fixed kernel\n", rc); return 0; } printf("write: OK (32KB) - buggy kernel accepted a 2-bad-column write\n"); sleep(1); rc = lseek(fd, OFF, SEEK_SET); if (rc < 0) { perror("lseek2"); return 2; } rc = read(fd, rbuf, LEN); if (rc != LEN) { perror("read"); printf("RESULT: read FAILED (%d)\n", rc); return 0; } bad = 0; for (i = 0; i < 32 * 512; i++) if (rbuf[i] != 0xAA) { bad = 1; break; } for (i = 32 * 512; i < LEN; i++) if (rbuf[i] != 0x55) { bad = 1; break; } if (bad) { printf("RESULT: *** SILENT DATA LOSS: readback != written ***\n"); printf(" block1[0..3]=%02x %02x (want aa aa), block2[0..3]=%02x %02x (want 55 55)\n", rbuf[0], rbuf[1], rbuf[32*512], rbuf[32*512+1]); return 1; } printf("RESULT: readback matches (no data loss)\n"); return 0; } |