diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2024-09-12 09:10:47 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2024-09-12 09:10:47 +0000 |
commit | 37261180a7519229aa7700e0fed5d12311c51883 (patch) | |
tree | 0a308b4a4982cfcd5ccd907f488c454b114a164b /sys | |
parent | a39709c1b8a729d982aa82a85b8965c9e268d189 (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.c | 33 |
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; |