public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: bschubert@ddn.com
Cc: miklos@szeredi.hu, neal@gompa.dev, linux-fsdevel@vger.kernel.org,
	bernd@bsbernd.com, joannelkoong@gmail.com
Subject: Re: [PATCH 02/13] mount_service: add systemd/inetd socket service mounting helper
Date: Mon, 13 Apr 2026 18:00:01 -0700	[thread overview]
Message-ID: <20260414010001.GA149953@frogsfrogsfrogs> (raw)
In-Reply-To: <177577270253.2064074.4846020380322993537.stgit@frogsfrogsfrogs>

On Thu, Apr 09, 2026 at 03:21:04PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
> 
> Create a mount helper program that can start a fuse server that runs as
> a socket-based systemd service, and a new libfuse module to wrap all the
> details of communicating between the mount helper and the containerized
> fuse server.
> 
> This enables untrusted ext4 mounts via systemd service containers, which
> avoids the problem of malicious filesystems compromising the integrity
> of the running kernel through memory corruption.
> 
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
>  include/fuse_service.h                           |  243 ++++
>  include/fuse_service_priv.h                      |  133 ++
>  lib/mount_common_i.h                             |    3 
>  util/mount_service.h                             |   39 +
>  .github/workflows/install-ubuntu-dependencies.sh |   12 
>  doc/fuservicemount3.8                            |   24 
>  doc/meson.build                                  |    3 
>  include/meson.build                              |    4 
>  lib/fuse_service.c                               | 1099 +++++++++++++++++++
>  lib/fuse_service_stub.c                          |  106 ++
>  lib/fuse_versionscript                           |   17 
>  lib/helper.c                                     |   51 +
>  lib/meson.build                                  |   17 
>  lib/mount.c                                      |   12 
>  meson.build                                      |   34 +
>  meson_options.txt                                |    9 
>  util/fuservicemount.c                            |   18 
>  util/meson.build                                 |    9 
>  util/mount_service.c                             | 1304 ++++++++++++++++++++++
>  19 files changed, 3132 insertions(+), 5 deletions(-)
>  create mode 100644 include/fuse_service.h
>  create mode 100644 include/fuse_service_priv.h
>  create mode 100644 util/mount_service.h
>  create mode 100644 doc/fuservicemount3.8
>  create mode 100644 lib/fuse_service.c
>  create mode 100644 lib/fuse_service_stub.c
>  create mode 100644 util/fuservicemount.c
>  create mode 100644 util/mount_service.c
> 
> 

<snip>

> diff --git a/lib/fuse_service.c b/lib/fuse_service.c
> new file mode 100644
> index 00000000000000..b775727e7c91e2
> --- /dev/null
> +++ b/lib/fuse_service.c

<snip>

> +static int negotiate_hello(struct fuse_service *sf)
> +{
> +	struct fuse_service_hello hello = { };
> +	struct fuse_service_hello_reply reply = {
> +		.p.magic = htonl(FUSE_SERVICE_HELLO_REPLY),
> +		.version = htons(FUSE_SERVICE_PROTO),
> +	};
> +	struct iovec iov = {
> +		.iov_base = &hello,
> +		.iov_len = sizeof(hello),
> +	};
> +	struct msghdr msg = {
> +		.msg_iov = &iov,
> +		.msg_iovlen = 1,
> +	};
> +	uint64_t flags;

This is a u32 in the socket protocol.

> +	ssize_t size;
> +
> +	size = recvmsg(sf->sockfd, &msg, MSG_TRUNC);
> +	if (size < 0) {
> +		int error = errno;
> +
> +		fuse_log(FUSE_LOG_ERR, "fuse: receive service hello: %s\n",
> +			 strerror(error));
> +		return -error;
> +	}
> +	if (size != sizeof(hello)) {
> +		fuse_log(FUSE_LOG_ERR, "fuse: wrong service hello size %zd, expected %zd\n",
> +			 size, sizeof(hello));
> +		return -EBADMSG;
> +	}
> +
> +	if (ntohl(hello.p.magic) != FUSE_SERVICE_HELLO_CMD) {
> +		fuse_log(FUSE_LOG_ERR, "fuse: service server did not send hello command\n");
> +		return -EBADMSG;
> +	}
> +
> +	if (ntohs(hello.min_version) < FUSE_SERVICE_MIN_PROTO) {
> +		fuse_log(FUSE_LOG_ERR, "fuse: unsupported min service protocol version %u\n",
> +			ntohs(hello.min_version));
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (ntohs(hello.max_version) > FUSE_SERVICE_MAX_PROTO) {
> +		fuse_log(FUSE_LOG_ERR, "fuse: unsupported max service protocol version %u\n",
> +			ntohs(hello.min_version));
> +		return -EOPNOTSUPP;
> +	}
> +
> +	flags = ntohl(hello.flags);

Need to check for unrecognized bits being set here.

<snip>

> diff --git a/util/mount_service.c b/util/mount_service.c
> new file mode 100644
> index 00000000000000..abe88a0710255b
> --- /dev/null
> +++ b/util/mount_service.c

<snip>

> +static int mount_service_handle_fsopen_cmd(struct mount_service *mo,
> +					   const struct fuse_service_packet *p,
> +					   size_t psz)
> +{
> +	struct fuse_service_string_command *oc =
> +			container_of(p, struct fuse_service_string_command, p);
> +
> +	if (psz < sizeof_fuse_service_string_command(1)) {
> +		fprintf(stderr, "%s: fsopen command too small\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	if (!check_null_endbyte(p, psz)) {
> +		fprintf(stderr, "%s: fsopen command must be null terminated\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	if (mo->fstype) {
> +		fprintf(stderr, "%s: fstype respecified!\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	mo->fstype = strdup(oc->value);

It occurs to me -- the user that ran fuservicemount already *told* us
what the fuse subtype is going to be; they supplied it via the -t
option.  There's no need to pass the subtype to the fuse server only to
make it pass the subtype back to us.  The only thing that the fuse
server needs to tell us is if this is actually a fuseblk server, since
(among other things) it has to be able to *deal* with weird fuseblk
quirks, such as relying on the kernel to prevent other processes from
writing to the block device.

Therefore, the mount_service can just make a copy of the subtype string
from the cli arguments.  The fuse server can tell us if it wants fuseblk
mode (as a flag, instead of copying strings around!).  For the fsmount()
code path, it can set subtype= to the subtype string copied earlier.
For the classic mount() code, it can format "fuse{,blk}.$subtype" into a
string and pass that in.

<snip>

> +static int mount_service_handle_mount_cmd(struct mount_service *mo,
> +					  struct fuse_service_packet *p,
> +					  size_t psz)
> +{
> +	struct stat stbuf;
> +	struct fuse_service_mount_command *oc =
> +			container_of(p, struct fuse_service_mount_command, p);
> +	int ret;
> +
> +	if (psz != sizeof(struct fuse_service_mount_command)) {
> +		fprintf(stderr, "%s: mount command wrong size\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	if (!mo->fstype) {
> +		fprintf(stderr, "%s: missing mount type parameter\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	if (!mo->source) {
> +		fprintf(stderr, "%s: missing mount source parameter\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	if (!mo->mountpoint) {
> +		fprintf(stderr, "%s: missing mount point parameter\n",
> +			mo->msgtag);
> +		return mount_service_send_reply(mo, EINVAL);
> +	}
> +
> +	/*
> +	 * Make sure we can access the mountpoint and that it's either a
> +	 * directory or a regular file.  Linux can handle mounting atop special
> +	 * files, but we don't care to do such crazy things.
> +	 */
> +	ret = fstat(mo->mountfd, &stbuf);
> +	if (ret < 0) {
> +		int error = errno;
> +
> +		fprintf(stderr, "%s: %s: %s\n",
> +			mo->msgtag, mo->mountpoint, strerror(error));
> +		return mount_service_send_reply(mo, error);
> +	}
> +
> +	/* Make sure the mountpoint type matches what the caller wanted */
> +	switch (ntohs(oc->expected_fmt)) {

Now that the mountpoint command opens the mount point, the expected
format bits of the mountpoint should be passed along with the mountpoint
command and checked earlier.  The permission checks need to stay where
they are, though.

--D

  reply	other threads:[~2026-04-14  1:00 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-09 22:20 [PATCHSET v4] libfuse: run fuse servers as a contained service Darrick J. Wong
2026-04-09 22:20 ` [PATCH 01/13] Refactor mount code / move common functions to mount_util.c Darrick J. Wong
2026-04-09 22:21 ` [PATCH 02/13] mount_service: add systemd/inetd socket service mounting helper Darrick J. Wong
2026-04-14  1:00   ` Darrick J. Wong [this message]
2026-04-14 23:48   ` Darrick J. Wong
2026-04-17 23:19   ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 03/13] mount_service: create high level fuse helpers Darrick J. Wong
2026-04-14 23:58   ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 04/13] mount_service: use the new mount api for the mount service Darrick J. Wong
2026-04-17 22:03   ` Darrick J. Wong
2026-04-09 22:21 ` [PATCH 05/13] mount_service: update mtab after a successful mount Darrick J. Wong
2026-04-09 22:22 ` [PATCH 06/13] util: hoist the fuse.conf parsing and setuid mode enforcement code Darrick J. Wong
2026-04-09 22:22 ` [PATCH 07/13] util: fix checkpatch complaints in fuser_conf.[ch] Darrick J. Wong
2026-04-09 22:22 ` [PATCH 08/13] mount_service: enable unprivileged users in the same manner as fusermount Darrick J. Wong
2026-04-14 23:53   ` Darrick J. Wong
2026-04-17 22:01     ` Darrick J. Wong
2026-04-09 22:22 ` [PATCH 09/13] mount.fuse3: integrate systemd service startup Darrick J. Wong
2026-04-17 22:41   ` Darrick J. Wong
2026-04-09 22:23 ` [PATCH 10/13] mount_service: allow installation as a setuid program Darrick J. Wong
2026-04-09 22:23 ` [PATCH 11/13] example/service_ll: create a sample systemd service fuse server Darrick J. Wong
2026-04-14 23:56   ` Darrick J. Wong
2026-04-17 21:56   ` Darrick J. Wong
2026-04-09 22:23 ` [PATCH 12/13] example/service: create a sample systemd service for a high-level " Darrick J. Wong
2026-04-09 22:23 ` [PATCH 13/13] nullfs: support fuse systemd service mode Darrick J. Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260414010001.GA149953@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=bernd@bsbernd.com \
    --cc=bschubert@ddn.com \
    --cc=joannelkoong@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=miklos@szeredi.hu \
    --cc=neal@gompa.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox