From owner-freebsd-bugs@FreeBSD.ORG Tue Mar 4 18:10:01 2014 Return-Path: Delivered-To: freebsd-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 0E81E5A6 for ; Tue, 4 Mar 2014 18:10:01 +0000 (UTC) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id DF6A437D for ; Tue, 4 Mar 2014 18:10:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.8/8.14.8) with ESMTP id s24IA0Ox017275 for ; Tue, 4 Mar 2014 18:10:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.8/8.14.8/Submit) id s24IA0Na017274; Tue, 4 Mar 2014 18:10:00 GMT (envelope-from gnats) Resent-Date: Tue, 4 Mar 2014 18:10:00 GMT Resent-Message-Id: <201403041810.s24IA0Na017274@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Kris Moore Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 618F13AF for ; Tue, 4 Mar 2014 18:05:51 +0000 (UTC) Received: from cgiserv.freebsd.org (cgiserv.freebsd.org [IPv6:2001:1900:2254:206a::50:4]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4DA7B358 for ; Tue, 4 Mar 2014 18:05:51 +0000 (UTC) Received: from cgiserv.freebsd.org ([127.0.1.6]) by cgiserv.freebsd.org (8.14.8/8.14.8) with ESMTP id s24I5ohA062435 for ; Tue, 4 Mar 2014 18:05:50 GMT (envelope-from nobody@cgiserv.freebsd.org) Received: (from nobody@localhost) by cgiserv.freebsd.org (8.14.8/8.14.8/Submit) id s24I5oVH062432; Tue, 4 Mar 2014 18:05:50 GMT (envelope-from nobody) Message-Id: <201403041805.s24I5oVH062432@cgiserv.freebsd.org> Date: Tue, 4 Mar 2014 18:05:50 GMT From: Kris Moore To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: misc/187261: FUSE kernel panic when using socket / bind X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Mar 2014 18:10:01 -0000 >Number: 187261 >Category: misc >Synopsis: FUSE kernel panic when using socket / bind >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Mar 04 18:10:00 UTC 2014 >Closed-Date: >Last-Modified: >Originator: Kris Moore >Release: 10.0-RELEASE >Organization: iXsystems >Environment: FreeBSD krisdesktop 10.0-RELEASE FreeBSD 10.0-RELEASE-p4 #0: Tue Jan 14 20:48:07 UTC 2014 root@amd64-builder.pcbsd.org:/usr/obj/usr/src/sys/GENERIC amd64 >Description: I've run across an interesting bug in our fuse implementation. It looks like whenever a program running on the FUSE layer tries to create a socket() and then use bind(), it will immediately trigger a kernel panic. This is very likely the source of a number of fuse related kernel panics. >How-To-Repeat: I've attached an example to let you trigger this bug. Extract the archive and then compile "fusexmp.c" and socktest.c % cc -Wall `pkg-config fuse --cflags --libs` fusexmp.c -o fusexmp % cc socktest.c -o socktest Now mount the fuse passthrough filesystem, chroot and run the socktest program. You should see an immediate kernel panic. # ./fusexmp /mnt # chroot /mnt # cd # ./socktest >Fix: The kernel panic messages refer to fuse_vnop_create() being the culprit, located in sys/fs/fuse/fuse_vnops.c Patch attached with submission follows: # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # . # ./fusexmp.c # ./socktest.c # echo c - . mkdir -p . > /dev/null 2>&1 echo x - ./fusexmp.c sed 's/^X//' >./fusexmp.c << 'e2b4462e60c1b7270abe13927239aced' X/* X FUSE: Filesystem in Userspace X Copyright (C) 2001-2007 Miklos Szeredi X X This program can be distributed under the terms of the GNU GPL. X See the file COPYING. X X gcc -Wall `pkg-config fuse --cflags --libs` fusexmp.c -o fusexmp X*/ X X#define FUSE_USE_VERSION 26 X X#ifdef HAVE_CONFIG_H X#include X#endif X X#ifdef linux X/* For pread()/pwrite() */ X#define _XOPEN_SOURCE 500 X#endif X X#include X#include X#include X#include X#include X#include X#include X#include X#ifdef HAVE_SETXATTR X#include X#endif X Xstatic int xmp_getattr(const char *path, struct stat *stbuf) X{ X int res; X X res = lstat(path, stbuf); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_access(const char *path, int mask) X{ X int res; X X res = access(path, mask); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_readlink(const char *path, char *buf, size_t size) X{ X int res; X X res = readlink(path, buf, size - 1); X if (res == -1) X return -errno; X X buf[res] = '\0'; X return 0; X} X X Xstatic int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, X off_t offset, struct fuse_file_info *fi) X{ X DIR *dp; X struct dirent *de; X X (void) offset; X (void) fi; X X dp = opendir(path); X if (dp == NULL) X return -errno; X X while ((de = readdir(dp)) != NULL) { X struct stat st; X memset(&st, 0, sizeof(st)); X st.st_ino = de->d_ino; X st.st_mode = de->d_type << 12; X if (filler(buf, de->d_name, &st, 0)) X break; X } X X closedir(dp); X return 0; X} X Xstatic int xmp_mknod(const char *path, mode_t mode, dev_t rdev) X{ X int res; X X /* On Linux this could just be 'mknod(path, mode, rdev)' but this X is more portable */ X if (S_ISREG(mode)) { X res = open(path, O_CREAT | O_EXCL | O_WRONLY, mode); X if (res >= 0) X res = close(res); X } else if (S_ISFIFO(mode)) X res = mkfifo(path, mode); X else X res = mknod(path, mode, rdev); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_mkdir(const char *path, mode_t mode) X{ X int res; X X res = mkdir(path, mode); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_unlink(const char *path) X{ X int res; X X res = unlink(path); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_rmdir(const char *path) X{ X int res; X X res = rmdir(path); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_symlink(const char *from, const char *to) X{ X int res; X X res = symlink(from, to); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_rename(const char *from, const char *to) X{ X int res; X X res = rename(from, to); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_link(const char *from, const char *to) X{ X int res; X X res = link(from, to); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_chmod(const char *path, mode_t mode) X{ X int res; X X res = chmod(path, mode); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_chown(const char *path, uid_t uid, gid_t gid) X{ X int res; X X res = lchown(path, uid, gid); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_truncate(const char *path, off_t size) X{ X int res; X X res = truncate(path, size); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_utimens(const char *path, const struct timespec ts[2]) X{ X int res; X struct timeval tv[2]; X X tv[0].tv_sec = ts[0].tv_sec; X tv[0].tv_usec = ts[0].tv_nsec / 1000; X tv[1].tv_sec = ts[1].tv_sec; X tv[1].tv_usec = ts[1].tv_nsec / 1000; X X res = utimes(path, tv); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_open(const char *path, struct fuse_file_info *fi) X{ X int res; X X res = open(path, fi->flags); X if (res == -1) X return -errno; X X close(res); X return 0; X} X Xstatic int xmp_read(const char *path, char *buf, size_t size, off_t offset, X struct fuse_file_info *fi) X{ X int fd; X int res; X X (void) fi; X fd = open(path, O_RDONLY); X if (fd == -1) X return -errno; X X res = pread(fd, buf, size, offset); X if (res == -1) X res = -errno; X X close(fd); X return res; X} X Xstatic int xmp_write(const char *path, const char *buf, size_t size, X off_t offset, struct fuse_file_info *fi) X{ X int fd; X int res; X X (void) fi; X fd = open(path, O_WRONLY); X if (fd == -1) X return -errno; X X res = pwrite(fd, buf, size, offset); X if (res == -1) X res = -errno; X X close(fd); X return res; X} X Xstatic int xmp_statfs(const char *path, struct statvfs *stbuf) X{ X int res; X X res = statvfs(path, stbuf); X if (res == -1) X return -errno; X X return 0; X} X Xstatic int xmp_release(const char *path, struct fuse_file_info *fi) X{ X /* Just a stub. This method is optional and can safely be left X unimplemented */ X X (void) path; X (void) fi; X return 0; X} X Xstatic int xmp_fsync(const char *path, int isdatasync, X struct fuse_file_info *fi) X{ X /* Just a stub. This method is optional and can safely be left X unimplemented */ X X (void) path; X (void) isdatasync; X (void) fi; X return 0; X} X X#ifdef HAVE_SETXATTR X/* xattr operations are optional and can safely be left unimplemented */ Xstatic int xmp_setxattr(const char *path, const char *name, const char *value, X size_t size, int flags) X{ X int res = lsetxattr(path, name, value, size, flags); X if (res == -1) X return -errno; X return 0; X} X Xstatic int xmp_getxattr(const char *path, const char *name, char *value, X size_t size) X{ X int res = lgetxattr(path, name, value, size); X if (res == -1) X return -errno; X return res; X} X Xstatic int xmp_listxattr(const char *path, char *list, size_t size) X{ X int res = llistxattr(path, list, size); X if (res == -1) X return -errno; X return res; X} X Xstatic int xmp_removexattr(const char *path, const char *name) X{ X int res = lremovexattr(path, name); X if (res == -1) X return -errno; X return 0; X} X#endif /* HAVE_SETXATTR */ X Xstatic struct fuse_operations xmp_oper = { X .getattr = xmp_getattr, X .access = xmp_access, X .readlink = xmp_readlink, X .readdir = xmp_readdir, X .mknod = xmp_mknod, X .mkdir = xmp_mkdir, X .symlink = xmp_symlink, X .unlink = xmp_unlink, X .rmdir = xmp_rmdir, X .rename = xmp_rename, X .link = xmp_link, X .chmod = xmp_chmod, X .chown = xmp_chown, X .truncate = xmp_truncate, X .utimens = xmp_utimens, X .open = xmp_open, X .read = xmp_read, X .write = xmp_write, X .statfs = xmp_statfs, X .release = xmp_release, X .fsync = xmp_fsync, X#ifdef HAVE_SETXATTR X .setxattr = xmp_setxattr, X .getxattr = xmp_getxattr, X .listxattr = xmp_listxattr, X .removexattr = xmp_removexattr, X#endif X}; X Xint main(int argc, char *argv[]) X{ X umask(0); X return fuse_main(argc, argv, &xmp_oper, NULL); X} e2b4462e60c1b7270abe13927239aced echo x - ./socktest.c sed 's/^X//' >./socktest.c << 'eea2de23f17c2563cae55bb24c3ec03f' X#include X#include X#include X#include X#include X#include X#include X#include X#include X Xint main(int argc, char **argv) X{ X char path[FILENAME_MAX]; X strcpy(path, "/tmp/socket.sock"); X X union { X struct sockaddr_un u; X struct sockaddr s; X } adr = { .u = {0} }; X X int sock = socket(PF_UNIX, SOCK_STREAM, 0); X if ( sock < 0 ) X { X perror("socket"); X return -1; X } X X adr.u.sun_family = AF_UNIX; X strncpy(adr.u.sun_path, path, sizeof (adr.u.sun_path) -1 ); X if ( bind (sock, &adr.s, sizeof(adr) ) < 0 ) X { X perror("bind"); X return -1; X } X X return 0; X X} eea2de23f17c2563cae55bb24c3ec03f exit >Release-Note: >Audit-Trail: >Unformatted: