>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"

Reply via email to