misc/187261: FUSE kernel panic when using socket / bind
Kris Moore
kris at pcbsd.org
Tue Mar 4 18:10:01 UTC 2014
>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 at 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 <miklos at 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:
More information about the freebsd-bugs
mailing list