aboutsummaryrefslogtreecommitdiffstats
path: root/lib/fuse_lowlevel.c
diff options
context:
space:
mode:
authorBernd Schubert <bernd@bsbernd.com>2025-07-15 20:09:17 +0200
committerBernd Schubert <bernd@bsbernd.com>2025-07-16 10:45:41 +0200
commitd8253770ac2cf4b8769e8cf41eb3c629f30ee80f (patch)
tree8a50a1f787bad12b6c27fae834db4375dbc12efb /lib/fuse_lowlevel.c
parent5f6d3be57f73c8b79e9b616b0f30464475116084 (diff)
downloadlibfuse-d8253770ac2cf4b8769e8cf41eb3c629f30ee80f.tar.gz
Avoid double unmount on FUSE_DESTROY
This is a long standing issue, a system could have unmounted /path/to/mnt and then fuse-client/kernel would send FUSE_DESTROY, which would then again try a umount. Given that FUSE_DESTROY is async, that umount might arrive any time later and might possibly unmount a wrong mount point. A warning as in issue #1286 is just minor to that. Code wise this uses atomics to free the char *, as FUSE_DESTROY might race with a signal and a double free might be possible without proctection. A lock might run into the same issue, if the signal would arrive at the wrong time a double lock would be possible. Additionally, fuse_session_mount() is updated, to first duplicatate the pointer and to then do the kernel mount - reverting the kernel mount in case of strdup() failure is much harder. Closes: https://github.com/libfuse/libfuse/issues/1286 Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
Diffstat (limited to 'lib/fuse_lowlevel.c')
-rw-r--r--lib/fuse_lowlevel.c33
1 files changed, 22 insertions, 11 deletions
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 1ae3473..9731b44 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -2921,11 +2921,15 @@ static void _do_destroy(fuse_req_t req, const fuse_ino_t nodeid,
const void *op_in, const void *in_payload)
{
struct fuse_session *se = req->se;
+ char *mountpoint;
(void) nodeid;
(void)op_in;
(void)in_payload;
+ mountpoint = atomic_exchange(&se->mountpoint, NULL);
+ free(mountpoint);
+
se->got_destroy = 1;
se->got_init = 0;
if (se->op.destroy)
@@ -4206,15 +4210,23 @@ int fuse_session_custom_io_30(struct fuse_session *se,
offsetof(struct fuse_custom_io, clone_fd), fd);
}
-int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
+int fuse_session_mount(struct fuse_session *se, const char *_mountpoint)
{
int fd;
+ char *mountpoint;
- if (mountpoint == NULL) {
+ if (_mountpoint == NULL) {
fuse_log(FUSE_LOG_ERR, "Invalid null-ptr mountpoint!\n");
return -1;
}
+ mountpoint = strdup(_mountpoint);
+ if (mountpoint == NULL) {
+ fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for mountpoint. Error: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
/*
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
* would ensue.
@@ -4237,7 +4249,7 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
fuse_log(FUSE_LOG_ERR,
"fuse: Invalid file descriptor /dev/fd/%u\n",
fd);
- return -1;
+ goto error_out;
}
se->fd = fd;
return 0;
@@ -4246,18 +4258,16 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
/* Open channel */
fd = fuse_kern_mount(mountpoint, se->mo);
if (fd == -1)
- return -1;
+ goto error_out;
se->fd = fd;
/* Save mountpoint */
- se->mountpoint = strdup(mountpoint);
- if (se->mountpoint == NULL)
- goto error_out;
+ se->mountpoint = mountpoint;
return 0;
error_out:
- fuse_kern_unmount(mountpoint, fd);
+ free(mountpoint);
return -1;
}
@@ -4269,10 +4279,11 @@ int fuse_session_fd(struct fuse_session *se)
void fuse_session_unmount(struct fuse_session *se)
{
if (se->mountpoint != NULL) {
- fuse_kern_unmount(se->mountpoint, se->fd);
+ char *mountpoint = atomic_exchange(&se->mountpoint, NULL);
+
+ fuse_kern_unmount(mountpoint, se->fd);
se->fd = -1;
- free(se->mountpoint);
- se->mountpoint = NULL;
+ free(mountpoint);
}
}