summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2024-09-12 09:10:47 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2024-09-12 09:10:47 +0000
commit37261180a7519229aa7700e0fed5d12311c51883 (patch)
tree0a308b4a4982cfcd5ccd907f488c454b114a164b /sys
parenta39709c1b8a729d982aa82a85b8965c9e268d189 (diff)
Do a basic sanity check that dirents returned via fuse are kind of sane.
Ensure that file names passed back by readdir do not include a '/' character. The '/' char is the path separator and is not allowed in any filename. On top of this also check that d_reclen and d_namlen are kind of sane and zero out the padding bytes after d_name. OK beck@
Diffstat (limited to 'sys')
-rw-r--r--sys/miscfs/fuse/fuse_vnops.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/sys/miscfs/fuse/fuse_vnops.c b/sys/miscfs/fuse/fuse_vnops.c
index 8c38ab55c41..c1592e7f5e7 100644
--- a/sys/miscfs/fuse/fuse_vnops.c
+++ b/sys/miscfs/fuse/fuse_vnops.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fuse_vnops.c,v 1.69 2024/05/13 11:17:40 semarie Exp $ */
+/* $OpenBSD: fuse_vnops.c,v 1.70 2024/09/12 09:10:46 claudio Exp $ */
/*
* Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com>
*
@@ -752,6 +752,8 @@ fusefs_readdir(void *v)
struct fusefs_node *ip;
struct fusefs_mnt *fmp;
struct fusebuf *fbuf;
+ struct dirent *dp;
+ char *edp;
struct vnode *vp;
struct proc *p;
struct uio *uio;
@@ -806,6 +808,35 @@ fusefs_readdir(void *v)
break;
}
+ /* validate the returned dirents */
+ dp = (struct dirent *)fbuf->fb_dat;
+ edp = fbuf->fb_dat + fbuf->fb_len;
+ while ((char *)dp < edp) {
+ if ((char *)dp + offsetof(struct dirent, d_name) >= edp
+ || dp->d_reclen <= offsetof(struct dirent, d_name)
+ || (char *)dp + dp->d_reclen > edp) {
+ error = EINVAL;
+ break;
+ }
+ if (dp->d_namlen + offsetof(struct dirent, d_name) >=
+ dp->d_reclen) {
+ error = EINVAL;
+ break;
+ }
+ memset(dp->d_name + dp->d_namlen, 0, dp->d_reclen -
+ dp->d_namlen - offsetof(struct dirent, d_name));
+
+ if (memchr(dp->d_name, '/', dp->d_namlen) != NULL) {
+ error = EINVAL;
+ break;
+ }
+ dp = (struct dirent *)((char *)dp + dp->d_reclen);
+ }
+ if (error) {
+ fb_delete(fbuf);
+ break;
+ }
+
if ((error = uiomove(fbuf->fb_dat, fbuf->fb_len, uio))) {
fb_delete(fbuf);
break;