KERNEL BUG: lseek() broken on raw devices

Matthias Andree ma at dt.e-technik.uni-dortmund.de
Sat Sep 6 10:38:11 PDT 2003


Hi,

I just figured FreeBSD will happily write data (disk blocks) from byte
#0 even after successful lseek(fd, 17, SEEK_SET) returned 17, at least
on da(4) partitions such as rda0s1f. Tested on 4.9-PRERELEASE checked
out early Sep 5th.

I'd suggest that lseek to a non-sector boundary returns either (off_t)-1
and EINVAL (preferred) or at least returns the actual position (though
few applications will handle that properly), or that write() returns -1
and EINVAL when writing to such a non-aligned location.

Try this DESTRUCTIVE test on a scratch or spare partition that may lose
all its data.

Note that some devices are halfway fine; vn0c lets the seek succeed but
returns EINVAL for the subsequent write, so make sure to try this on a
da partition (comment out your swap partition, reboot, and use your
freshly-deactivated swap partition if need be).

NIT PICK: write(2) EINVAL means "The pointer associated with d was
negative." (d is the file descriptor)

Expected behaviour: either abort with "Invalid argument" on lseek bs/2
or on write or print "OK".

#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 = open(argc > 1 ? argv[1] : "/dev/rda0s1f", O_RDWR);
        int want = 0x66, t;
        struct disklabel dl;
        char *x;

        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;
}


-- 
Matthias Andree

Encrypt your mail: my GnuPG key ID is 0x052E7D95


More information about the freebsd-stable mailing list