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: 5d8b9a39a1 ("mount_service: add systemd socket service mounting helper")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
This commit is contained in:
Darrick J. Wong
2026-06-08 17:15:00 -07:00
committed by Bernd Schubert
parent d4e199cb63
commit dc6ce72129
6 changed files with 1 additions and 129 deletions
-8
View File
@@ -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.
-5
View File
@@ -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);
-51
View File
@@ -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)
-5
View File
@@ -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)
{
}
-1
View File
@@ -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;
+1 -59
View File
@@ -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);