>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 r...@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 <pathtosock> # ./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 <mik...@szeredi.hu> 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 <config.h> X#endif X X#ifdef linux X/* For pread()/pwrite() */ X#define _XOPEN_SOURCE 500 X#endif X X#include <fuse.h> X#include <stdio.h> X#include <string.h> X#include <unistd.h> X#include <fcntl.h> X#include <dirent.h> X#include <errno.h> X#include <sys/time.h> X#ifdef HAVE_SETXATTR X#include <sys/xattr.h> 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 <stdio.h> X#include <errno.h> X#include <stdlib.h> X#include <unistd.h> X#include <string.h> X#include <sys/socket.h> X#include <sys/types.h> X#include <sys/un.h> X#include <signal.h> 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: _______________________________________________ freebsd-bugs@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-bugs To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"