libfuse/fuse_daemonize:  wait until daemon child process is ready (#55)

Mounting a FUSE file system remotely using SSH in combination with
pseudo-terminal allocation (-t), results in "Transport endpoint is
not connected" errors when trying to access the file system contents.

For example:

  # ssh -t root@localhost  "cmsfs-fuse /dev/disk/by-path/ccw-0.0.0190 /CMSFS"
  Connection to localhost closed.
  # ls /CMSFS
  ls: cannot access '/CMSFS': Transport endpoint is not connected

The cmsfs-fuse main program (which can also be any other FUSE file
system) calls into the fuse_main() libfuse library function.
The fuse_main() function later calls fuse_daemonize() to fork the
daemon process to handle the FUSE file system I/O.

The fuse_daemonize() function calls fork() as usual.  The child
proceeds with setsid() and then redirecting its file descriptors
to /dev/null etc.  The parent process, simply exits.

The child's functions and the parent's exit creates a subtle race.
This is seen with an SSH connection.  The SSH command above calls
cmsfs-fuse on an allocated pseudo-terminal device (-t option).

If the parent exits, SSH receives the command completion and closes
the connection, that means, it closes the master side of the
pseudo-terminal.  This causes a HUP signal being sent to the process
group on the pseudo-terminal.  At this point in time, the child might
not have completed the setsid() call and, hence, becomes terminated.
Note that fuse daemon sets up its signal handlers after fuse_daemonize()
has completed.

Even if the child has the chance to disassociate from its parent process
group to become it's own process group with setsid(), the child still
has the pseudo-terminal opened as stdin, stdout, and stderr.  So the
pseudo-terminal still behave as controlling terminal and might cause a
SIGHUP at closing the the master side.

To solve the problem, the parent has to wait until the child (the fuse
daemon process) has completed its processing, that means, has become
its own process group with setsid() and closed any file descriptors
pointing to the pseudo-terminal.

Closes: #27

Reported-by: Ofer Baruch <oferba@il.ibm.com>
Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
2 files changed
tree: e8c2cf0b8d7d5cfad41e2ea92cb5475e9e69cab7
  1. doc/
  2. example/
  3. include/
  4. lib/
  5. test/
  6. util/
  7. .gitignore
  8. AUTHORS
  9. ChangeLog
  10. configure.ac
  11. COPYING
  12. COPYING.LIB
  13. fuse.pc.in
  14. makeconf.sh
  15. Makefile.am
  16. NEWS
  17. README.md
  18. README.NFS
README.md

libfuse

Warning: unresolved security issue

Be aware that FUSE has an unresolved security bug (bug #15): the permission check for accessing a cached directory is only done once when the directory entry is first loaded into the cache. Subsequent accesses will re-use the results of the first check, even if the directory permissions have since changed, and even if the subsequent access is made by a different user.

This bug needs to be fixed in the Linux kernel and has been known since 2006 but unfortunately no fix has been applied yet. If you depend on correct permission handling for FUSE file systems, the only workaround is to completely disable caching of directory entries. Alternatively, the severity of the bug can be somewhat reduced by not using the allow_other mount option.

About

FUSE (Filesystem in Userspace) is an interface for userspace programs to export a filesystem to the Linux kernel. The FUSE project consists of two components: the fuse kernel module (maintained in the regular kernel repositories) and the libfuse userspace library (maintained in this repository). libfuse provides the reference implementation for communicating with the FUSE kernel module.

A FUSE file system is typically implemented as a standalone application that links with libfuse. libfuse provides functions to mount the file system, unmount it, read requests from the kernel, and send responses back. libfuse offers two APIs: a "high-level", synchronous API, and a "low-level" asynchronous API. In both cases, incoming requests from the kernel are passed to the main program using callbacks. When using the high-level API, the callbacks may work with file names and paths instead of inodes, and processing of a request finishes when the callback function returns. When using the low-level API, the callbacks must work with inodes and responses must be sent explicitly using a separate set of API functions.

Installation

./configure
make -j8
make install

You may also need to add /usr/local/lib to /etc/ld.so.conf and/or run ldconfig. If you're building from the git repository (instead of using a release tarball), you also need to run ./makeconf.sh to create the configure script.

You'll also need a fuse kernel module (Linux kernels 2.6.14 or later contain FUSE support).

For more details see the file INSTALL

Security implications

If you run make install, the fusermount program is installed set-user-id to root. This is done to allow normal users to mount their own filesystem implementations.

There must however be some limitations, in order to prevent Bad User from doing nasty things. Currently those limitations are:

  • The user can only mount on a mountpoint, for which it has write permission

  • The mountpoint is not a sticky directory which isn't owned by the user (like /tmp usually is)

  • No other user (including root) can access the contents of the mounted filesystem (though this can be relaxed by allowing the use of the allow_other and allow_root mount options in fuse.conf)

Building your own filesystem

FUSE comes with several example file systems in the examples directory. For example, the fusexmp example mirrors the contents of the root directory under the mountpoint. Start from there and adapt the code!

The documentation of the API functions and necessary callbacks is mostly contained in the files include/fuse.h (for the high-level API) and include/fuse_lowlevel.h (for the low-level API). An autogenerated html version of the API is available in the doc/html directory and at http://libfuse.github.io/doxygen.

Getting Help

If you need help, please ask on the fuse-devel@lists.sourceforge.net mailing list (subscribe at https://lists.sourceforge.net/lists/listinfo/fuse-devel).

Please report any bugs on the GitHub issue tracker at https://github.com/libfuse/main/issues.