Re: [PATCH 02/13] mount_service: add systemd/inetd socket service mounting helper

Darrick J. Wong
2026-04-14T01:00:02Z

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