From dc6ce72129edafa0fc551cd14af2999040e3c5a5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 8 Jun 2026 17:15:00 -0700 Subject: [PATCH] libfuse: remove unmount command from the service api Bernd received a complaint via github that the "unmount" command in the new fuse service API is prone to malicious symlink creation races because the umount2 call doesn't use UMOUNT_NOFOLLOW. Therefore, if you can trick a fuse service into asking the mount service helper to undo the mount just after you've replaced the mount with a symlink to a sensitive mount (e.g. /sys) then you whack a system hard. I don't know why this unmounting functionality exists in libfuse, and apparently Bernd doesn't like it. Since the fuse service API has not yet been released, let's just rip out the command and pretend it never existed. Fixes: 5d8b9a39a1cb3e ("mount_service: add systemd socket service mounting helper") Signed-off-by: "Darrick J. Wong" --- include/fuse_service.h | 8 ----- include/fuse_service_priv.h | 5 ---- lib/fuse_service.c | 51 ------------------------------- lib/fuse_service_stub.c | 5 ---- lib/fuse_versionscript | 1 - util/mount_service.c | 60 +------------------------------------ 6 files changed, 1 insertion(+), 129 deletions(-) diff --git a/include/fuse_service.h b/include/fuse_service.h index 0536312d..d6aedea8 100644 --- a/include/fuse_service.h +++ b/include/fuse_service.h @@ -218,14 +218,6 @@ int fuse_service_session_mount(struct fuse_service *sf, struct fuse_session *se, mode_t expected_fmt, struct fuse_cmdline_opts *opts); -/** - * Ask the mount helper to unmount th e filesystem. - * - * @param sf service context - * @return 0 on success, or negative errno on failure - */ -int fuse_service_session_unmount(const struct fuse_service *sf); - /** * Bid farewell to the mount.service helper. It is still necessary to call * fuse_service_destroy after this. diff --git a/include/fuse_service_priv.h b/include/fuse_service_priv.h index cfbc444b..988f7c92 100644 --- a/include/fuse_service_priv.h +++ b/include/fuse_service_priv.h @@ -37,7 +37,6 @@ struct fuse_service_memfd_argv { #define FUSE_SERVICE_MNTOPTS_CMD 0x4f505453 /* OPTS */ #define FUSE_SERVICE_MNTPT_CMD 0x4d4e5450 /* MNTP */ #define FUSE_SERVICE_MOUNT_CMD 0x444f4954 /* DOIT */ -#define FUSE_SERVICE_UNMOUNT_CMD 0x554d4e54 /* UMNT */ #define FUSE_SERVICE_BYE_CMD 0x42594545 /* BYEE */ #define FUSE_SERVICE_MTABOPTS_CMD 0x4d544142 /* MTAB */ @@ -150,10 +149,6 @@ struct fuse_service_mount_command { uint32_t ms_flags; }; -struct fuse_service_unmount_command { - struct fuse_service_packet p; -}; - int fuse_parse_cmdline_service(struct fuse_args *args, struct fuse_cmdline_opts *opts); diff --git a/lib/fuse_service.c b/lib/fuse_service.c index 2d7232c3..b42e6b53 100644 --- a/lib/fuse_service.c +++ b/lib/fuse_service.c @@ -1128,57 +1128,6 @@ out_strings: return ret; } -int fuse_service_session_unmount(const struct fuse_service *sf) -{ - struct fuse_service_simple_reply reply = { }; - struct fuse_service_unmount_command c = { - .p.magic = htonl(FUSE_SERVICE_UNMOUNT_CMD), - }; - ssize_t size; - - /* already gone? */ - if (sf->sockfd < 0) - return 0; - - size = __send_packet(sf, &c, sizeof(c)); - if (size < 0) { - int error = errno; - - fuse_log(FUSE_LOG_ERR, "fuse: send service unmount: %s\n", - strerror(error)); - return -error; - } - - size = __recv_packet(sf, &reply, sizeof(reply)); - if (size < 0) { - int error = errno; - - fuse_log(FUSE_LOG_ERR, "fuse: service unmount reply: %s\n", - strerror(error)); - return -error; - } - if (size != sizeof(reply)) { - fuse_log(FUSE_LOG_ERR, "fuse: wrong service unmount reply size %zd, expected %zd\n", - size, sizeof(reply)); - return -EBADMSG; - } - - if (ntohl(reply.p.magic) != FUSE_SERVICE_SIMPLE_REPLY) { - fuse_log(FUSE_LOG_ERR, "fuse: service unmount reply contains wrong magic!\n"); - return -EBADMSG; - } - - if (reply.error) { - int error = ntohl(reply.error); - - fuse_log(FUSE_LOG_ERR, "fuse: service unmount: %s\n", - strerror(error)); - return -error; - } - - return 0; -} - void fuse_service_release(struct fuse_service *sf) { if (sf->owns_fusedevfd) diff --git a/lib/fuse_service_stub.c b/lib/fuse_service_stub.c index 44aef47e..668c2598 100644 --- a/lib/fuse_service_stub.c +++ b/lib/fuse_service_stub.c @@ -93,11 +93,6 @@ int fuse_service_session_mount(struct fuse_service *sf, struct fuse_session *se, return -EOPNOTSUPP; } -int fuse_service_session_unmount(const struct fuse_service *sf) -{ - return -EOPNOTSUPP; -} - void fuse_service_release(struct fuse_service *sf) { } diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index ab792090..e8dc0066 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -250,7 +250,6 @@ FUSE_3.19 { fuse_service_request_blockdev; fuse_service_send_goodbye; fuse_service_session_mount; - fuse_service_session_unmount; fuse_req_secctx_count; fuse_req_secctx_reset; fuse_req_secctx_next; diff --git a/util/mount_service.c b/util/mount_service.c index 6a6a8b60..d4f57630 100644 --- a/util/mount_service.c +++ b/util/mount_service.c @@ -42,6 +42,7 @@ #ifdef HAVE_NEW_MOUNT_API #include "mount_i_linux.h" #endif +#include "fuse_mount_compat.h" struct mount_service { /* prefix for printing error messages */ @@ -1722,62 +1723,6 @@ static int mount_service_handle_mount_cmd(struct mount_service *mo, return mount_service_regular_mount(mo, oc, &stbuf); } -static int mount_service_handle_unmount_cmd(struct mount_service *mo, - struct fuse_service_packet *p, - size_t psz) -{ - int ret; - - (void)p; - - if (psz != sizeof(struct fuse_service_unmount_command)) { - fprintf(stderr, "%s: unmount command wrong size %zu, expected %zu\n", - mo->msgtag, psz, sizeof(struct fuse_service_unmount_command)); - return mount_service_send_reply(mo, EINVAL); - } - - if (!mo->mounted) { - fprintf(stderr, "%s: will not umount before successful mount!\n", - mo->msgtag); - return mount_service_send_reply(mo, EINVAL); - } - - ret = chdir("/"); - if (ret) { - int error = errno; - - fprintf(stderr, "%s: fuse server failed chdir: %s\n", - mo->msgtag, strerror(error)); - return mount_service_send_reply(mo, error); - } - - close(mo->mountfd); - mo->mountfd = -1; - - /* - * Try to unmount the resolved mountpoint, and hope that we're not the - * victim of a race. - */ - ret = umount2(mo->resv_mountpoint, MNT_DETACH); - if (ret) { - int error = errno; - - fprintf(stderr, "%s: fuse server failed unmount: %s\n", - mo->msgtag, strerror(error)); - return mount_service_send_reply(mo, error); - } - - /* - * The unmount succeeded, so we send a positive reply even if the mtab - * update fails. - */ - if (have_real_mtabopts(mo)) - fuse_mnt_remove_mount(mo->msgtag, mo->resv_mountpoint); - - mo->mounted = false; - return mount_service_send_reply(mo, 0); -} - static int mount_service_handle_bye_cmd(const struct mount_service *mo, struct fuse_service_packet *p, size_t psz) @@ -1921,9 +1866,6 @@ int mount_service_main(int argc, char *argv[]) case FUSE_SERVICE_MOUNT_CMD: ret = mount_service_handle_mount_cmd(&mo, p, sz); break; - case FUSE_SERVICE_UNMOUNT_CMD: - ret = mount_service_handle_unmount_cmd(&mo, p, sz); - break; case FUSE_SERVICE_BYE_CMD: ret = mount_service_handle_bye_cmd(&mo, p, sz); free(p);