diff mbox series

[12/25] virtiofsd: check input buffer size in fuse_lowlevel.c ops

Message ID 20191024112718.34657-13-dgilbert@redhat.com (mailing list archive)
State New, archived
Headers show
Series virtiofs daemon (security) | expand

Commit Message

Dr. David Alan Gilbert Oct. 24, 2019, 11:27 a.m. UTC
From: Stefan Hajnoczi <stefanha@redhat.com>

Each FUSE operation involves parsing the input buffer.  Currently the
code assumes the input buffer is large enough for the expected
arguments.  This patch uses fuse_mbuf_iter to check the size.

Most operations are simple to convert.  Some are more complicated due to
variable-length inputs or different sizes depending on the protocol
version.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 contrib/virtiofsd/fuse_lowlevel.c | 621 +++++++++++++++++++++++-------
 1 file changed, 482 insertions(+), 139 deletions(-)
diff mbox series

Patch

diff --git a/contrib/virtiofsd/fuse_lowlevel.c b/contrib/virtiofsd/fuse_lowlevel.c
index 7927348398..d032e411e1 100644
--- a/contrib/virtiofsd/fuse_lowlevel.c
+++ b/contrib/virtiofsd/fuse_lowlevel.c
@@ -15,6 +15,7 @@ 
 #include "fuse_misc.h"
 #include "fuse_virtio.h"
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -27,7 +28,6 @@ 
 
 
 
-#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
 #define OFFSET_MAX 0x7fffffffffffffffLL
 
 struct fuse_pollhandle {
@@ -705,9 +705,14 @@  int fuse_reply_poll(fuse_req_t req, unsigned revents)
 	return send_reply_ok(req, &arg, sizeof(arg));
 }
 
-static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	char *name = (char *) inarg;
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+	if (!name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.lookup)
 		req->se->op.lookup(req, nodeid, name);
@@ -715,9 +720,16 @@  static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_forget(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg;
+	struct fuse_forget_in *arg;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.forget)
 		req->se->op.forget(req, nodeid, arg->nlookup);
@@ -726,20 +738,47 @@  static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 }
 
 static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
-			    const void *inarg)
+			    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_batch_forget_in *arg = (void *) inarg;
-	struct fuse_forget_one *param = (void *) PARAM(arg);
-	unsigned int i;
+	struct fuse_batch_forget_in *arg;
+	struct fuse_forget_data *forgets;
+	size_t scount;
 
 	(void) nodeid;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_none(req);
+	}
+
+	/* Prevent integer overflow.  The compiler emits the following warning
+	 * unless we use the scount local variable:
+	 *
+	 * error: comparison is always false due to limited range of data type
+	 * [-Werror=type-limits]
+	 *
+	 * This may be true on 64-bit hosts but we need this check for 32-bit
+	 * hosts.
+	 */
+	scount = arg->count;
+	if (scount > SIZE_MAX / sizeof(forgets[0])) {
+		fuse_reply_none(req);
+		return;
+	}
+
+	forgets = fuse_mbuf_iter_advance(iter, arg->count *
+					 sizeof(forgets[0]));
+	if (!forgets) {
+		fuse_reply_none(req);
+		return;
+	}
+
 	if (req->se->op.forget_multi) {
-		req->se->op.forget_multi(req, arg->count,
-				     (struct fuse_forget_data *) param);
+		req->se->op.forget_multi(req, arg->count, forgets);
 	} else if (req->se->op.forget) {
+		unsigned int i;
+
 		for (i = 0; i < arg->count; i++) {
-			struct fuse_forget_one *forget = &param[i];
 			struct fuse_req *dummy_req;
 
 			dummy_req = fuse_ll_alloc_req(req->se);
@@ -750,8 +789,8 @@  static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
 			dummy_req->ctx = req->ctx;
 			dummy_req->ch = NULL;
 
-			req->se->op.forget(dummy_req, forget->nodeid,
-					  forget->nlookup);
+			req->se->op.forget(dummy_req, forgets[i].ino,
+					   forgets[i].nlookup);
 		}
 		fuse_reply_none(req);
 	} else {
@@ -759,13 +798,20 @@  static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid,
 	}
 }
 
-static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
 	struct fuse_file_info *fip = NULL;
 	struct fuse_file_info fi;
 
 	if (req->se->conn.proto_minor >= 9) {
-		struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg;
+		struct fuse_getattr_in *arg;
+
+		arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+		if (!arg) {
+			fuse_reply_err(req, EINVAL);
+			return;
+		}
 
 		if (arg->getattr_flags & FUSE_GETATTR_FH) {
 			memset(&fi, 0, sizeof(fi));
@@ -780,14 +826,21 @@  static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg;
-
 	if (req->se->op.setattr) {
+		struct fuse_setattr_in *arg;
 		struct fuse_file_info *fi = NULL;
 		struct fuse_file_info fi_store;
 		struct stat stbuf;
+
+		arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+		if (!arg) {
+			fuse_reply_err(req, EINVAL);
+			return;
+		}
+
 		memset(&stbuf, 0, sizeof(stbuf));
 		convert_attr(arg, &stbuf);
 		if (arg->valid & FATTR_FH) {
@@ -812,9 +865,16 @@  static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_access(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	struct fuse_access_in *arg = (struct fuse_access_in *) inarg;
+	struct fuse_access_in *arg;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.access)
 		req->se->op.access(req, nodeid, arg->mask);
@@ -822,9 +882,10 @@  static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid,
+			struct fuse_mbuf_iter *iter)
 {
-	(void) inarg;
+	(void) iter;
 
 	if (req->se->op.readlink)
 		req->se->op.readlink(req, nodeid);
@@ -832,15 +893,24 @@  static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
-	char *name = PARAM(arg);
+	bool compat = req->se->conn.proto_minor < 12;
+	struct fuse_mknod_in *arg;
+	const char *name;
 
-	if (req->se->conn.proto_minor >= 12)
+	arg = fuse_mbuf_iter_advance(iter,
+			compat ? FUSE_COMPAT_MKNOD_IN_SIZE : sizeof(*arg));
+	name = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	if (!compat) {
 		req->ctx.umask = arg->umask;
-	else
-		name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
+	}
 
 	if (req->se->op.mknod)
 		req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
@@ -848,22 +918,40 @@  static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
+	bool compat = req->se->conn.proto_minor < 12;
+	struct fuse_mkdir_in *arg;
+	const char *name;
 
-	if (req->se->conn.proto_minor >= 12)
+	arg = fuse_mbuf_iter_advance(iter,
+			compat ? sizeof(uint32_t) : sizeof(*arg));
+	name = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	if (!compat) {
 		req->ctx.umask = arg->umask;
+	}
 
 	if (req->se->op.mkdir)
-		req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
+		req->se->op.mkdir(req, nodeid, name, arg->mode);
 	else
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	char *name = (char *) inarg;
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+
+	if (!name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.unlink)
 		req->se->op.unlink(req, nodeid, name);
@@ -871,9 +959,15 @@  static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	char *name = (char *) inarg;
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+
+	if (!name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.rmdir)
 		req->se->op.rmdir(req, nodeid, name);
@@ -881,10 +975,16 @@  static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	char *name = (char *) inarg;
-	char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1;
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+	const char *linkname = fuse_mbuf_iter_advance_str(iter);
+
+	if (!name || !linkname) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.symlink)
 		req->se->op.symlink(req, linkname, nodeid, name);
@@ -892,11 +992,20 @@  static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_rename(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg;
-	char *oldname = PARAM(arg);
-	char *newname = oldname + strlen(oldname) + 1;
+	struct fuse_rename_in *arg;
+	const char *oldname;
+	const char *newname;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	oldname = fuse_mbuf_iter_advance_str(iter);
+	newname = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !oldname || !newname) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.rename)
 		req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
@@ -905,11 +1014,20 @@  static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_rename2(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
-	char *oldname = PARAM(arg);
-	char *newname = oldname + strlen(oldname) + 1;
+	struct fuse_rename2_in *arg;
+	const char *oldname;
+	const char *newname;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	oldname = fuse_mbuf_iter_advance_str(iter);
+	newname = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !oldname || !newname) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.rename)
 		req->se->op.rename(req, nodeid, oldname, arg->newdir, newname,
@@ -918,42 +1036,65 @@  static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_link(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_link_in *arg = (struct fuse_link_in *) inarg;
+	struct fuse_link_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+
+	if (!arg || !name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.link)
-		req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg));
+		req->se->op.link(req, arg->oldnodeid, nodeid, name);
 	else
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_create(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	struct fuse_create_in *arg = (struct fuse_create_in *) inarg;
-
 	if (req->se->op.create) {
+		bool compat = req->se->conn.proto_minor < 12;
+		struct fuse_create_in *arg;
 		struct fuse_file_info fi;
-		char *name = PARAM(arg);
+		const char *name;
+
+		arg = fuse_mbuf_iter_advance(iter, compat ?
+				sizeof(struct fuse_open_in) :
+				sizeof(*arg));
+		name = fuse_mbuf_iter_advance_str(iter);
+		if (!arg || !name) {
+			fuse_reply_err(req, EINVAL);
+			return;
+		}
 
 		memset(&fi, 0, sizeof(fi));
 		fi.flags = arg->flags;
 
-		if (req->se->conn.proto_minor >= 12)
+		if (!compat) {
 			req->ctx.umask = arg->umask;
-		else
-			name = (char *) inarg + sizeof(struct fuse_open_in);
+		}
 
 		req->se->op.create(req, nodeid, name, arg->mode, &fi);
 	} else
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_open(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+	struct fuse_open_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.flags = arg->flags;
 
@@ -963,16 +1104,21 @@  static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_open(req, &fi);
 }
 
-static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_read(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
-
 	if (req->se->op.read) {
+		bool compat = req->se->conn.proto_minor < 9;
+		struct fuse_read_in *arg;
 		struct fuse_file_info fi;
 
+		arg = fuse_mbuf_iter_advance(iter, compat ?
+				offsetof(struct fuse_read_in, lock_owner) :
+				sizeof(*arg));
+
 		memset(&fi, 0, sizeof(fi));
 		fi.fh = arg->fh;
-		if (req->se->conn.proto_minor >= 9) {
+		if (!compat) {
 			fi.lock_owner = arg->lock_owner;
 			fi.flags = arg->flags;
 		}
@@ -981,22 +1127,34 @@  static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_write(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
+	bool compat = req->se->conn.proto_minor < 9;
+	struct fuse_write_in *arg;
 	struct fuse_file_info fi;
-	char *param;
+	const char *param;
+
+	arg = fuse_mbuf_iter_advance(iter, compat ?
+			FUSE_COMPAT_WRITE_IN_SIZE : sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	param = fuse_mbuf_iter_advance(iter, arg->size);
+	if (!param) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 	fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0;
 
-	if (req->se->conn.proto_minor < 9) {
-		param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
-	} else {
+	if (!compat) {
 		fi.lock_owner = arg->lock_owner;
 		fi.flags = arg->flags;
-		param = PARAM(arg);
 	}
 
 	if (req->se->op.write)
@@ -1062,16 +1220,27 @@  static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid,
 	se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi);
 }
 
-static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_flush(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
+	bool compat = req->se->conn.proto_minor < 7;
+	struct fuse_flush_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, compat ?
+			offsetof(struct fuse_flush_in, lock_owner) :
+			sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 	fi.flush = 1;
-	if (req->se->conn.proto_minor >= 7)
+	if (!compat) {
 		fi.lock_owner = arg->lock_owner;
+	}
 
 	if (req->se->op.flush)
 		req->se->op.flush(req, nodeid, &fi);
@@ -1079,21 +1248,31 @@  static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_release(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+	bool compat = req->se->conn.proto_minor < 8;
+	struct fuse_release_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, compat ?
+			offsetof(struct fuse_release_in, lock_owner) :
+			sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.flags = arg->flags;
 	fi.fh = arg->fh;
-	if (req->se->conn.proto_minor >= 8) {
+	if (!compat) {
 		fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
 		fi.lock_owner = arg->lock_owner;
-	}
-	if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
-		fi.flock_release = 1;
-		fi.lock_owner = arg->lock_owner;
+
+		if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) {
+			fi.flock_release = 1;
+		}
 	}
 
 	if (req->se->op.release)
@@ -1102,11 +1281,19 @@  static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, 0);
 }
 
-static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+	struct fuse_fsync_in *arg;
 	struct fuse_file_info fi;
-	int datasync = arg->fsync_flags & 1;
+	int datasync;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+	datasync = arg->fsync_flags & 1;
 
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
@@ -1120,11 +1307,18 @@  static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_open_in *arg = (struct fuse_open_in *) inarg;
+	struct fuse_open_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.flags = arg->flags;
 
@@ -1134,11 +1328,18 @@  static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_open(req, &fi);
 }
 
-static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+	struct fuse_read_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 
@@ -1148,11 +1349,18 @@  static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid,
+			   struct fuse_mbuf_iter *iter)
 {
-	struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
+	struct fuse_read_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 
@@ -1162,11 +1370,18 @@  static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid,
+			  struct fuse_mbuf_iter *iter)
 {
-	struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
+	struct fuse_release_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.flags = arg->flags;
 	fi.fh = arg->fh;
@@ -1177,11 +1392,19 @@  static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, 0);
 }
 
-static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid,
+			struct fuse_mbuf_iter *iter)
 {
-	struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg;
+	struct fuse_fsync_in *arg;
 	struct fuse_file_info fi;
-	int datasync = arg->fsync_flags & 1;
+	int datasync;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+	datasync = arg->fsync_flags & 1;
 
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
@@ -1192,10 +1415,11 @@  static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
 	(void) nodeid;
-	(void) inarg;
+	(void) iter;
 
 	if (req->se->op.statfs)
 		req->se->op.statfs(req, nodeid);
@@ -1208,11 +1432,25 @@  static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	}
 }
 
-static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid,
+			struct fuse_mbuf_iter *iter)
 {
-	struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
-	char *name = PARAM(arg);
-	char *value = name + strlen(name) + 1;
+	struct fuse_setxattr_in *arg;
+	const char *name;
+	const char *value;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	name = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	value = fuse_mbuf_iter_advance(iter, arg->size);
+	if (!value) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.setxattr)
 		req->se->op.setxattr(req, nodeid, name, value, arg->size,
@@ -1221,19 +1459,35 @@  static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid,
+			struct fuse_mbuf_iter *iter)
 {
-	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+	struct fuse_getxattr_in *arg;
+	const char *name;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	name = fuse_mbuf_iter_advance_str(iter);
+	if (!arg || !name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.getxattr)
-		req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size);
+		req->se->op.getxattr(req, nodeid, name, arg->size);
 	else
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid,
+			 struct fuse_mbuf_iter *iter)
 {
-	struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+	struct fuse_getxattr_in *arg;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.listxattr)
 		req->se->op.listxattr(req, nodeid, arg->size);
@@ -1241,9 +1495,15 @@  static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid,
+			   struct fuse_mbuf_iter *iter)
 {
-	char *name = (char *) inarg;
+	const char *name = fuse_mbuf_iter_advance_str(iter);
+
+	if (!name) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.removexattr)
 		req->se->op.removexattr(req, nodeid, name);
@@ -1265,12 +1525,19 @@  static void convert_fuse_file_lock(struct fuse_file_lock *fl,
 	flock->l_pid = fl->pid;
 }
 
-static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+	struct fuse_lk_in *arg;
 	struct fuse_file_info fi;
 	struct flock flock;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 	fi.lock_owner = arg->owner;
@@ -1283,12 +1550,18 @@  static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 }
 
 static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
-			    const void *inarg, int sleep)
+			    struct fuse_mbuf_iter *iter, int sleep)
 {
-	struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+	struct fuse_lk_in *arg;
 	struct fuse_file_info fi;
 	struct flock flock;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 	fi.lock_owner = arg->owner;
@@ -1323,14 +1596,16 @@  static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid,
 	}
 }
 
-static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	do_setlk_common(req, nodeid, inarg, 0);
+	do_setlk_common(req, nodeid, iter, 0);
 }
 
-static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid,
+		      struct fuse_mbuf_iter *iter)
 {
-	do_setlk_common(req, nodeid, inarg, 1);
+	do_setlk_common(req, nodeid, iter, 1);
 }
 
 static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
@@ -1372,12 +1647,20 @@  static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
 	return 0;
 }
 
-static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid,
+			 struct fuse_mbuf_iter *iter)
 {
-	struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg;
+	struct fuse_interrupt_in *arg;
 	struct fuse_session *se = req->se;
 
 	(void) nodeid;
+
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	if (se->debug)
 		fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
 			(unsigned long long) arg->unique);
@@ -1415,9 +1698,15 @@  static struct fuse_req *check_interrupt(struct fuse_session *se,
 		return NULL;
 }
 
-static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg;
+	struct fuse_bmap_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
 	if (req->se->op.bmap)
 		req->se->op.bmap(req, nodeid, arg->blocksize, arg->block);
@@ -1425,19 +1714,35 @@  static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid,
+		     struct fuse_mbuf_iter *iter)
 {
-	struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
-	unsigned int flags = arg->flags;
-	void *in_buf = arg->in_size ? PARAM(arg) : NULL;
+	struct fuse_ioctl_in *arg;
+	unsigned int flags;
+	void *in_buf = NULL;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	flags = arg->flags;
 	if (flags & FUSE_IOCTL_DIR &&
 	    !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) {
 		fuse_reply_err(req, ENOTTY);
 		return;
 	}
 
+	if (arg->in_size) {
+		in_buf = fuse_mbuf_iter_advance(iter, arg->in_size);
+		if (!in_buf) {
+			fuse_reply_err(req, EINVAL);
+			return;
+		}
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 
@@ -1459,11 +1764,18 @@  void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
 	free(ph);
 }
 
-static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_poll(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
+	struct fuse_poll_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 	fi.poll_events = arg->events;
@@ -1487,11 +1799,18 @@  static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	}
 }
 
-static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid,
+			 struct fuse_mbuf_iter *iter)
 {
-	struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
+	struct fuse_fallocate_in *arg;
 	struct fuse_file_info fi;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi, 0, sizeof(fi));
 	fi.fh = arg->fh;
 
@@ -1501,11 +1820,18 @@  static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg)
+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
+			       struct fuse_mbuf_iter *iter)
 {
-	struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg;
+	struct fuse_copy_file_range_in *arg;
 	struct fuse_file_info fi_in, fi_out;
 
+	arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
 	memset(&fi_in, 0, sizeof(fi_in));
 	fi_in.fh = arg->fh_in;
 
@@ -1522,15 +1848,34 @@  static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void
 		fuse_reply_err(req, ENOSYS);
 }
 
-static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_init(fuse_req_t req, fuse_ino_t nodeid,
+		    struct fuse_mbuf_iter *iter)
 {
-	struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+	size_t compat_size = offsetof(struct fuse_init_in, max_readahead);
+	struct fuse_init_in *arg;
 	struct fuse_init_out outarg;
 	struct fuse_session *se = req->se;
 	size_t bufsize = se->bufsize;
 	size_t outargsize = sizeof(outarg);
 
 	(void) nodeid;
+
+	/* First consume the old fields... */
+	arg = fuse_mbuf_iter_advance(iter, compat_size);
+	if (!arg) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
+
+	/* ...and now consume the new fields. */
+	if (arg->major == 7 && arg->minor >= 6) {
+		if (!fuse_mbuf_iter_advance(iter, sizeof(*arg) -
+					    compat_size)) {
+			fuse_reply_err(req, EINVAL);
+			return;
+		}
+	}
+
 	if (se->debug) {
 		fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
 		if (arg->major == 7 && arg->minor >= 6) {
@@ -1744,12 +2089,13 @@  static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 	send_reply_ok(req, &outarg, outargsize);
 }
 
-static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid,
+		       struct fuse_mbuf_iter *iter)
 {
 	struct fuse_session *se = req->se;
 
 	(void) nodeid;
-	(void) inarg;
+	(void) iter;
 
 	se->got_destroy = 1;
 	if (se->op.destroy)
@@ -1934,7 +2280,7 @@  int fuse_req_interrupted(fuse_req_t req)
 }
 
 static struct {
-	void (*func)(fuse_req_t, fuse_ino_t, const void *);
+	void (*func)(fuse_req_t, fuse_ino_t, struct fuse_mbuf_iter *);
 	const char *name;
 } fuse_ll_ops[] = {
 	[FUSE_LOOKUP]	   = { do_lookup,      "LOOKUP"	     },
@@ -2013,7 +2359,6 @@  void fuse_session_process_buf_int(struct fuse_session *se,
 	const struct fuse_buf *buf = bufv->buf;
 	struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf);
 	struct fuse_in_header *in;
-	const void *inarg;
 	struct fuse_req *req;
 	int err;
 
@@ -2086,12 +2431,10 @@  void fuse_session_process_buf_int(struct fuse_session *se,
 			fuse_reply_err(intr, EAGAIN);
 	}
 
-	inarg = (void *) &in[1];
 	if (in->opcode == FUSE_WRITE && se->op.write_buf)
 		do_write_buf(req, in->nodeid, &iter, bufv);
 	else
-		fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
-
+		fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter);
 	return;
 
 reply_err: