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