Date: Wed, 17 Dec 2003 01:20:22 +0100 From: Matthias Andree <matthias.andree@gmx.de> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/60313: data destruction: lseek() misalignment silently ignored by some block devices Message-ID: <E1AWPQM-00006K-VN@libertas.emma.line.org> Resent-Message-ID: <200312170030.hBH0UKpR098825@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 60313 >Category: kern >Synopsis: data destruction: lseek() misalignment silently ignored by some block devices >Confidential: no >Severity: critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Dec 16 16:30:20 PST 2003 >Closed-Date: >Last-Modified: >Originator: Matthias Andree >Release: FreeBSD 4.9-STABLE i386 >Organization: >Environment: System: FreeBSD libertas.emma.line.org 4.9-STABLE FreeBSD 4.9-STABLE #65: Sat Dec 13 20:13:45 CET 2003 toor@libertas.emma.line.org:/usr/src/sys/compile/LIBERTAS i386 >Description: da(4) will happily return success on lseek() to some random position that is not aligned at a block boundary and start writing from the beginning, byte 0, of the block, ignoring the lseek(). In other words, if I have a drive with 512 bytes per block, seek to offset 256 and write 512 bytes, da(4) will scribble over the first 256 bytes as well. Either the seek or the write should be rejected. vn(4) will accept the seek but a subsequent write(2) will return the bogus EINVAL in errno. ad(4) status is unknown. FreeBSD 5 status is unknown. >How-To-Repeat: Save this test program as tryraw.c. Compile this DESTRUCTIVE test program and run it on an UNUSED partition: gcc -O -o tryraw tryraw.c ./tryraw /dev/da0s1b WARNING: THIS TEST DESTROYS DATA IN THE GIVEN PARTITION! USE IT ON AN INACTIVE SWAP PARTITION ONLY (i. e. comment the swap partition out, reboot). /* BEGIN tryraw.c */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/disklabel.h> void barf(const char *t) __attribute__((noreturn)); void barf(const char *t) { perror(t); exit(EXIT_FAILURE); } int main(int argc, char **argv) { int bs, fd; int want = 0x66, t; struct disklabel dl; char *x; if (argc < 2) { fprintf(stderr, "need a partition that I can clobber.\n"); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDWR); if (fd < 0) barf("open"); if (0 == ioctl(fd, DIOCGDINFO, &dl)) { printf("sector size: %lu\n", (unsigned long)(bs = dl.d_secsize)); } else barf("ioctl DIOCGDINFO"); /* this writes bs = blocksize times the byte 0x66 at offset #0 * then seeks to offset #bs/2 then writes bs times the byte 0x33. */ if (!(x = malloc(bs))) barf("malloc"); memset(x, want, bs); if (lseek(fd, 0, SEEK_SET) == (off_t)-1) barf("lseek 0"); if (write(fd, x, bs) < bs) barf("write"); memset(x, 0x33, bs); t = lseek(fd, bs >> 1, SEEK_SET); if (t == -1) barf("lseek bs/2"); printf("seeked to byte #%d\n", t); if (write(fd, x, bs) < bs) barf("write"); if (lseek(fd, 0, SEEK_SET) == (off_t)-1) barf("lseek 0"); if (read(fd, x, bs) < bs) barf("read"); if (x[0] == want) puts("OK"); else printf("KERNEL BUG: byte #0 is %x, should be %x\n", x[0], want); free(x); return 0; } /* END tryraw.c */ >Fix: Let lseek return (off_t)-1 and errno==EINVAL when the device doesn't support unaligned access. >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E1AWPQM-00006K-VN>