lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <176169814906.1428772.2925113261612522411.stgit@frogsfrogsfrogs>
Date: Tue, 28 Oct 2025 18:07:54 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: djwong@...nel.org, bschubert@....com
Cc: linux-ext4@...r.kernel.org, linux-fsdevel@...r.kernel.org,
 bernd@...ernd.com, miklos@...redi.hu, joannelkoong@...il.com, neal@...pa.dev
Subject: [PATCH 3/5] libfuse: delegate iomap privilege from mount.service to
 fuse services

From: Darrick J. Wong <djwong@...nel.org>

Enable the mount.service helper to attach whatever privileges it might
have to enable iomap to a /dev/fuse fd before passing that fd to the
fuse server.  Assuming that the fuse service itself does not have
sufficient privilege to enable iomap on its own, it can now inherit that
privilege via the fd.

Signed-off-by: "Darrick J. Wong" <djwong@...nel.org>
---
 include/fuse_kernel.h       |    1 +
 include/fuse_lowlevel.h     |   11 +++++++
 include/fuse_service.h      |   11 +++++++
 include/fuse_service_priv.h |   10 +++++++
 lib/fuse_lowlevel.c         |    5 +++
 lib/fuse_service.c          |   49 +++++++++++++++++++++++++++++++++
 lib/fuse_versionscript      |    2 +
 util/mount_service.c        |   64 +++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 152 insertions(+), 1 deletion(-)


diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 10bdf276ef9b74..0638d774d36cbc 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -1178,6 +1178,7 @@ struct fuse_iomap_support {
 #define FUSE_DEV_IOC_BACKING_OPEN	_IOW(FUSE_DEV_IOC_MAGIC, 1, \
 					     struct fuse_backing_map)
 #define FUSE_DEV_IOC_BACKING_CLOSE	_IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
+#define FUSE_DEV_IOC_ADD_IOMAP		_IO(FUSE_DEV_IOC_MAGIC, 99)
 #define FUSE_DEV_IOC_IOMAP_SUPPORT	_IOR(FUSE_DEV_IOC_MAGIC, 99, \
 					     struct fuse_iomap_support)
 
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index d79b7e1902b331..a93f3e27f6ef6d 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -2710,7 +2710,6 @@ bool fuse_req_is_uring(fuse_req_t req);
 int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
 			 void **mr);
 
-
 /**
  * Discover the kernel's iomap capabilities.  Returns FUSE_CAP_IOMAP_* flags.
  *
@@ -2720,6 +2719,16 @@ int fuse_req_get_payload(fuse_req_t req, char **payload, size_t *payload_sz,
  */
 uint64_t fuse_lowlevel_discover_iomap(int fd);
 
+/**
+ * Request that iomap capabilities be added to this fuse device.  This enables
+ * a privileged mount helper to convey the privileges that allow iomap usage to
+ * a completely unprivileged fuse server.
+ *
+ * @param fd open file descriptor to a fuse device
+ * @return 0 on success, -1 on failure with errno set
+ */
+int fuse_lowlevel_add_iomap(int fd);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/fuse_service.h b/include/fuse_service.h
index a852516feb39fb..47080a75bc9ab6 100644
--- a/include/fuse_service.h
+++ b/include/fuse_service.h
@@ -128,6 +128,17 @@ int fuse_service_receive_file(struct fuse_service *sf,
  */
 int fuse_service_finish_file_requests(struct fuse_service *sf);
 
+/**
+ * Attach iomap to the fuse connection.
+ *
+ * @param sf service context
+ * @param mandatory true if the server requires iomap
+ * @param error result of trying to enable iomap
+ * @return 0 on success, -1 on error
+ */
+int fuse_service_configure_iomap(struct fuse_service *sf, bool mandatory,
+				 int *error);
+
 /**
  * Ask the mount.service helper to mount the filesystem for us.  The fuse client
  * will begin sending requests to the fuse server immediately after this.
diff --git a/include/fuse_service_priv.h b/include/fuse_service_priv.h
index 042568e97e7e13..ce2e194ccf0be6 100644
--- a/include/fuse_service_priv.h
+++ b/include/fuse_service_priv.h
@@ -32,6 +32,7 @@ struct fuse_service_memfd_argv {
 #define FUSE_SERVICE_MNTPT_CMD		0x4d4e5450	/* MNTP */
 #define FUSE_SERVICE_MOUNT_CMD		0x444f4954	/* DOIT */
 #define FUSE_SERVICE_BYE_CMD		0x42594545	/* BYEE */
+#define FUSE_SERVICE_IOMAP_CMD		0x494f4d41	/* IOMA */
 
 /* mount.service sends replies to the fuse server */
 #define FUSE_SERVICE_OPEN_REPLY		0x46494c45	/* FILE */
@@ -72,6 +73,15 @@ static inline size_t sizeof_fuse_service_open_command(size_t pathlen)
 	return sizeof(struct fuse_service_open_command) + pathlen + 1;
 }
 
+#define FUSE_IOMAP_MODE_OPTIONAL	0x503F /* P? */
+#define FUSE_IOMAP_MODE_MANDATORY	0x5021 /* P! */
+
+struct fuse_service_iomap_command {
+	struct fuse_service_packet p;
+	__be16 mode;
+	__be16 padding;
+};
+
 struct fuse_service_string_command {
 	struct fuse_service_packet p;
 	char value[];
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 7eaa8e51f50129..51c609761494af 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -4990,3 +4990,8 @@ uint64_t fuse_lowlevel_discover_iomap(int fd)
 
 	return ios.flags;
 }
+
+int fuse_lowlevel_add_iomap(int fd)
+{
+	return ioctl(fd, FUSE_DEV_IOC_ADD_IOMAP);
+}
diff --git a/lib/fuse_service.c b/lib/fuse_service.c
index f627bdb94d9b0f..48633640c1c41b 100644
--- a/lib/fuse_service.c
+++ b/lib/fuse_service.c
@@ -629,6 +629,55 @@ static int send_mount(struct fuse_service *sf, unsigned int flags, int *error)
 	return 0;
 }
 
+int fuse_service_configure_iomap(struct fuse_service *sf, bool mandatory,
+				 int *error)
+{
+	struct fuse_service_iomap_command cmd = {
+		.p.magic = ntohl(FUSE_SERVICE_IOMAP_CMD),
+		.mode = mandatory ? ntohs(FUSE_IOMAP_MODE_MANDATORY) :
+				    ntohs(FUSE_IOMAP_MODE_OPTIONAL),
+	};
+	struct fuse_service_simple_reply reply = { };
+	struct iovec iov = {
+		.iov_base = &cmd,
+		.iov_len = sizeof(cmd),
+	};
+	struct msghdr msg = {
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+	ssize_t size;
+
+	size = sendmsg(sf->sockfd, &msg, MSG_EOR | MSG_NOSIGNAL);
+	if (size < 0) {
+		perror("fuse: send iomap command");
+		return -1;
+	}
+
+	iov.iov_base = &reply;
+	iov.iov_len = sizeof(reply);
+	size = recvmsg(sf->sockfd, &msg, MSG_TRUNC);
+	if (size < 0) {
+		perror("fuse: iomap command reply");
+		return -1;
+	}
+	if (size != sizeof(reply)) {
+		fprintf(stderr,
+ "fuse: wrong iomap command reply size %zd, expected %zd\n",
+			size, sizeof(reply));
+		return -1;
+	}
+
+	if (ntohl(reply.p.magic) != FUSE_SERVICE_SIMPLE_REPLY) {
+		fprintf(stderr,
+ "fuse: iomap command reply contains wrong magic!\n");
+		return -1;
+	}
+
+	*error = ntohl(reply.error);
+	return 0;
+}
+
 int fuse_service_mount(struct fuse_service *sf, struct fuse_session *se,
 		       const char *mountpoint)
 {
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 039150600fc556..2adab40e0eab1f 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -241,9 +241,11 @@ FUSE_3.99 {
 		fuse_lowlevel_notify_iomap_inval;
 		fuse_fs_iomap_upsert;
 		fuse_fs_iomap_inval;
+		fuse_lowlevel_add_iomap;
 
 		fuse_service_accept;
 		fuse_service_append_args;
+		fuse_service_configure_iomap;
 		fuse_service_destroy;
 		fuse_service_finish_file_requests;
 		fuse_service_mount;
diff --git a/util/mount_service.c b/util/mount_service.c
index dcaf055ae648f4..e3410d524167a4 100644
--- a/util/mount_service.c
+++ b/util/mount_service.c
@@ -62,6 +62,9 @@ struct mount_service {
 
 	/* fd for fsopen */
 	int fsopenfd;
+
+	/* did someone configure iomap already? */
+	int iomap_configured:1;
 };
 
 /* Filter out the subtype of the filesystem (e.g. fuse.Y -> Y) */
@@ -399,6 +402,22 @@ static int mount_service_send_file_error(struct mount_service *mo, int error,
 	return ret;
 }
 
+static int mount_service_config_iomap(struct mount_service *mo,
+				      bool mandatory)
+{
+	int ret;
+
+	mo->iomap_configured = 1;
+
+	ret = fuse_lowlevel_add_iomap(mo->fusedevfd);
+	if (ret && mandatory) {
+		perror("mount.service: adding iomap capability");
+		return -errno;
+	}
+
+	return 0;
+}
+
 static int mount_service_send_required_files(struct mount_service *mo,
 					     const char *fusedev)
 {
@@ -729,6 +748,13 @@ static int mount_service_regular_mount(struct mount_service *mo,
 		return -1;
 	}
 
+	/*
+	 * If nobody tried to configure iomap, try to enable it but don't
+	 * fail if we can't.
+	 */
+	if (!mo->iomap_configured)
+		mount_service_config_iomap(mo, false);
+
 	ret = mount(mo->source, mo->mountpoint, mo->fstype, ntohl(oc->flags),
 		    realmopts);
 	free(realmopts);
@@ -800,6 +826,41 @@ static int mount_service_fsopen_mount(struct mount_service *mo,
 	return mount_service_send_reply(mo, 0);
 }
 
+static int mount_service_handle_iomap_cmd(struct mount_service *mo,
+					  struct fuse_service_packet *p)
+{
+	struct fuse_service_iomap_command *oc =
+			container_of(p, struct fuse_service_iomap_command, p);
+	bool mandatory = false;
+	int ret;
+
+	if (oc->padding) {
+		fprintf(stderr, "mount.service: invalid iomap command\n");
+		mount_service_send_reply(mo, EINVAL);
+		return -1;
+	}
+
+	switch (ntohs(oc->mode)) {
+	case FUSE_IOMAP_MODE_MANDATORY:
+		mandatory = true;
+		/* fallthrough */
+	case FUSE_IOMAP_MODE_OPTIONAL:
+		ret = mount_service_config_iomap(mo, mandatory);
+		break;
+	default:
+		fprintf(stderr, "mount.service: invalid iomap command mode\n");
+		ret = -1;
+	}
+
+	if (ret < 0) {
+		mount_service_send_reply(mo, -ret);
+		return -1;
+	}
+
+	mount_service_send_reply(mo, 0);
+	return 0;
+}
+
 static int mount_service_handle_mount_cmd(struct mount_service *mo,
 					  struct fuse_service_packet *p)
 {
@@ -942,6 +1003,9 @@ int mount_service_main(int argc, char *argv[])
 		case FUSE_SERVICE_MNTPT_CMD:
 			ret = mount_service_handle_mountpoint_cmd(&mo, p);
 			break;
+		case FUSE_SERVICE_IOMAP_CMD:
+			ret = mount_service_handle_iomap_cmd(&mo, p);
+			break;
 		case FUSE_SERVICE_MOUNT_CMD:
 			ret = mount_service_handle_mount_cmd(&mo, p);
 			break;


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ