Bug#1055222: fuse: backport unprivileged usage via /dev/fd/N to support usage in namespaces

2023-11-06 Thread Helmut Grohne
Hi Laszlo,

On Mon, Nov 06, 2023 at 06:49:48PM +0100, László Böszörményi (GCS) wrote:
>  Just for the record, the change broke self-testing of gphotofs big on
> all architectures. It fails with:
> autopkgtest: test command1: [ "$(./gphotofs 2>&1)" == 'fuse: missing
> mountpoint parameter' ]
> 165s command1 FAIL non-zero exit status 1
> 
> Will try to check and fix it.

I'm sorry. I did not realize that at the time parse_fuse_fd is called
mountpoint could legitimately be a NULL pointer. This happens when
gphotofs is run without arguments. As a consequence, the library
segfaults there and the autopkgtest fails. parse_fuse_fd needs to handle
a NULL pointer. Patch attached.

Helmut
diff --minimal -Nru fuse-2.9.9/debian/changelog fuse-2.9.9/debian/changelog
--- fuse-2.9.9/debian/changelog 2023-11-05 13:44:31.0 +0100
+++ fuse-2.9.9/debian/changelog 2023-11-06 21:26:44.0 +0100
@@ -1,3 +1,10 @@
+fuse (2.9.9-7.1) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+  * Fix /dev/fd/N support to handle the absence of a mountpoint.
+
+ -- Helmut Grohne   Mon, 06 Nov 2023 21:26:44 +0100
+
 fuse (2.9.9-7) unstable; urgency=medium
 
   * Use no for Rules-Requires-Root.
diff --minimal -Nru fuse-2.9.9/debian/patches/0009-dev-fd.patch 
fuse-2.9.9/debian/patches/0009-dev-fd.patch
--- fuse-2.9.9/debian/patches/0009-dev-fd.patch 2023-11-05 13:44:31.0 
+0100
+++ fuse-2.9.9/debian/patches/0009-dev-fd.patch 2023-11-06 21:26:44.0 
+0100
@@ -24,7 +24,7 @@
 +{
 +  int fd = -1, len = 0;
 +
-+  if (sscanf(mountpoint, "/dev/fd/%u%n", , ) == 1 && len == 
strlen(mountpoint))
++  if (mountpoint && sscanf(mountpoint, "/dev/fd/%u%n", , ) == 1 && 
len == strlen(mountpoint))
 +  return fd;
 +  return -1;
 +}


Bug#1055222: fuse: backport unprivileged usage via /dev/fd/N to support usage in namespaces

2023-11-06 Thread GCS
Hi Helmut,

On Mon, Nov 6, 2023 at 11:28 AM Helmut Grohne  wrote:
> On Sun, Nov 05, 2023 at 02:19:18PM +0100, László Böszörményi (GCS) wrote:
> >  Yes, it makes upstreams more easily staying with the FUSE 2 API.
> > Making the switch to the FUSE 3 API more difficult. But OK, let it go.
> > I'm preparing the upload.
>
> Thank you.
 Just for the record, the change broke self-testing of gphotofs big on
all architectures. It fails with:
autopkgtest: test command1: [ "$(./gphotofs 2>&1)" == 'fuse: missing
mountpoint parameter' ]
165s command1 FAIL non-zero exit status 1

Will try to check and fix it.

Regards,
Laszlo/GCS



Bug#1055222: fuse: backport unprivileged usage via /dev/fd/N to support usage in namespaces

2023-11-06 Thread Helmut Grohne
Hi Laszlo,

On Sun, Nov 05, 2023 at 02:19:18PM +0100, László Böszörményi (GCS) wrote:
>  I think distributions should do the opposite, somehow enforce
> projects to migrate to the new, supported FUSE version. Basically it
> is deprecated since 2018 and as 2024 is approaching it means for six
> years.

I'm with you on this in principle. It is not going to happen by itself
though. I think you could help the situation by improving communication
about it. build-rdeps shows 88 rdeps for libfuse-dev. Let me propose
some options:

 * You could MBF those 88 rdeps with a normal bug asking them to move to
   fuse3. Chances are that a small fraction only needs to have their
   dependency updated. In other cases, you make the maintainer aware
   that there is a need for action.

 * You could reassign the package name libfuse-dev to fuse3 and rename
   the current libfuse-dev to libfuse2-dev. If you want to do this, I
   suggest that libfuse-dev starts providing libfuse2-dev now and that
   the MBF mentions this move. Then for each of those 88 packages it'll
   build with fuse3 or the maintainer has to make a choice between
   porting and changing the dependency to libfuse2-dev. While this may
   annoy some, it also gets people to think about the problem. After a
   while (say two months) you could move libfuse-dev to fuse3 and bump
   the remaining bugs to rc. Since there was a warning and lots of time,
   there won't be much grief.

 * I think given the rdeps it would be good to include fuse 2.x in
   trixie. But you could still announce that fuse 2.x is being deleted
   after trixie. If you do that now in that MBF, it'll serve as another
   clue about what to do. People won't be surprised when you file the RM
   bug for fuse in the forky cycle.

This is just a rough sketch. It probably needs to go through a d-devel
discussion (since it is a MBF), but doing this is relatively limited
work on your end with the prospect of getting rid of fuse 2.x within two
years. In particular, publishing such a schedule helps others plan and
communicate the porting need to upstreams.

>  Yes, it makes upstreams more easily staying with the FUSE 2 API.
> Making the switch to the FUSE 3 API more difficult. But OK, let it go.
> I'm preparing the upload.

Thank you.

Helmut



Bug#1055222: fuse: backport unprivileged usage via /dev/fd/N to support usage in namespaces

2023-11-05 Thread GCS
Hi Helmut,

On Thu, Nov 2, 2023 at 12:15 PM Helmut Grohne  wrote:
> I have a slightly unusual request. I know libfuse 2.x is a legacy in
> maintenance mode slated for removal eventually. Unfortunately, 2/3 of
> fuse users still use the 2.x branch so it seems like we will have to
> support it a bit longer than expected.
 I think distributions should do the opposite, somehow enforce
projects to migrate to the new, supported FUSE version. Basically it
is deprecated since 2018 and as 2024 is approaching it means for six
years.

> Unfortunately, this currently requires fuse >= 3.3 and e.g. fuse2fs from
> e2fsprogs still uses fuse 2.x. Last time Ted Ts'o was inquired about
> porting to 3.x, he was unenthusiastic and when I spent a look, it is
> quite non-trivial as the API changed a lot. It's not just fuse2fs that
> is affected but still 2/3 of drivers.
[...]
> So I think we should backport this into fuse 2.x. This is not to say
> that we should stop porting drivers to the 3.x API. It also slightly
> improves compatibility between 2.x and 3.x, so that seems like a sane
> trade-off to me. Do you agree?
 Yes, it makes upstreams more easily staying with the FUSE 2 API.
Making the switch to the FUSE 3 API more difficult. But OK, let it go.
I'm preparing the upload.

Regards,
Laszlo/GCS



Bug#1055222: fuse: backport unprivileged usage via /dev/fd/N to support usage in namespaces

2023-11-02 Thread Helmut Grohne
Package: libfuse2
Version: 2.9.9-6+b1
Tags: patch upstream fixed-upstream

Hello Laszlo,

I have a slightly unusual request. I know libfuse 2.x is a legacy in
maintenance mode slated for removal eventually. Unfortunately, 2/3 of
fuse users still use the 2.x branch so it seems like we will have to
support it a bit longer than expected.

You may have heared about Linux and namespaces and this is an area where
fuse < 3.3 falls short. While we think of mount as a privileged
operation in need of a setuid helper, you can perform an unprivileged
mount operation on standard Debian systems by employing user and mount
namespaces now. Getting there is a bit non-trivial, so let me explain
how it works with fuse >= 3.3. Typically, the goal is to perform the
mount operation in a different namespaces from the namespace running the
fuse userspace driver. Currently, one invokes a fuse driver with a mount
point and it first tries to mount directly and failing that executes a
setuid helper process fusermount to perform that operation with root
rights. Both happen in the same namespace as the driver process, which
is very much not what we want here. When launching the fusermount
process, it opens /dev/fuse, mounts it and communicates the fuse
connection via a unix domain socket using SCM_RIGHTS back to the driver
process. So what we really need here is a way of passing such a fuse
connection to a fuse driver and having it not perform the mount itself
(neither directly nor via fusermount). This need was recognized and
since fuse 3.3, the path /dev/fd/N is treated differently when passed as
a mountpoint. Upon seeing such a path, it'll skip all mounting and just
use the referenced file descriptor. Thus a process in a namespace can
open a fuse connection there, mount it and send back the file descriptor
(again using SCM_RIGHTS) to some other process outside the namespace
that ultimately launches the fuse driver while inheriting that file
descriptor.

Unfortunately, this currently requires fuse >= 3.3 and e.g. fuse2fs from
e2fsprogs still uses fuse 2.x. Last time Ted Ts'o was inquired about
porting to 3.x, he was unenthusiastic and when I spent a look, it is
quite non-trivial as the API changed a lot. It's not just fuse2fs that
is affected but still 2/3 of drivers. On the flip side, backporting this
function into fuse 2.x was relatively easy and the chances of breaking
something are fairly low as that path is intentionally so special that
people will only use it for this very functionality.

So I think we should backport this into fuse 2.x. This is not to say
that we should stop porting drivers to the 3.x API. It also slightly
improves compatibility between 2.x and 3.x, so that seems like a sane
trade-off to me. Do you agree?

I'm attaching a patch ready to be dropped into debian/patches for your
convenience.

Helmut
From: Helmut Grohne 
Subject: backport support for /dev/fd/NN
Origin: https://github.com/libfuse/libfuse/commit/64e11073b9347fcf9c6d1eea143763ba9e946f70
Forwarded: not-needed

This is a backport of 64e11073b9347fcf9c6d1eea143763ba9e946f70 ("Allow passing
`/dev/fuse` file descriptor from parent process") and enables fuse 2.9 to
perform the unprivileged part in much the same way as fuse >= 3.3 does. Since
fuse changed significantly, this is a reimplementation of the original idea on
the older code base.

An alternative to applying this patch is porting users of this library to 3.x.
In reality, progress has been slow and very many packages still use 2.x. While
such porting is a good thing in principle, it also is a huge task when a
relatively simple alternative is available.

--- fuse-2.9.9.orig/lib/mount.c
+++ fuse-2.9.9/lib/mount.c
@@ -575,6 +575,15 @@
 	return 0;
 }
 
+static int parse_fuse_fd(const char *mountpoint)
+{
+	int fd = -1, len = 0;
+
+	if (sscanf(mountpoint, "/dev/fd/%u%n", , ) == 1 && len == strlen(mountpoint))
+		return fd;
+	return -1;
+}
+
 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
 {
 	struct mount_opts mo;
@@ -604,6 +613,15 @@
 	if (mo.mtab_opts &&  fuse_opt_add_opt(_opts, mo.mtab_opts) == -1)
 		goto out;
 
+	res = parse_fuse_fd(mountpoint);
+	if (res >= 0) {
+		if (fcntl(res, F_GETFD) == -1) {
+			fprintf(stderr, "fuse: Invalid filesystem /dev/fd/%u\n", res);
+			res = -1;
+		}
+		goto out;
+	}
+
 	res = fuse_mount_sys(mountpoint, , mnt_opts);
 	if (res == -2) {
 		if (mo.fusermount_opts &&
--- fuse-2.9.9.orig/lib/helper.c
+++ fuse-2.9.9/lib/helper.c
@@ -105,7 +105,11 @@
 
 	case FUSE_OPT_KEY_NONOPT:
 		if (!hopts->mountpoint) {
+			int fd, len=0;
 			char mountpoint[PATH_MAX];
+			/* fuse_kern_mount matches /dev/fd/N */
+			if (sscanf(arg, "/dev/fd/%u%n", , ) == 1 && len == strlen(arg))
+return fuse_opt_add_opt(>mountpoint, arg);
 			if (realpath(arg, mountpoint) == NULL) {
 fprintf(stderr,
 	"fuse: bad mount point `%s': %s\n",