Ensure that directory entry padding bytes are zeroed.
Directory entries must be padded to maintain alignment; in many filesystems the padding was not initialized, resulting in stack memory being copied out to userspace. With the ino64 work there are also some explicit pad fields in struct dirent. Add a subroutine to clear these bytes and use it in the in-tree filesystems. The NFS client is omitted for now as it was fixed separately in r340787. Reported by: Thomas Barabosch, Fraunhofer FKIE Reviewed by: kib MFC after: 3 days Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
759436eb7c
commit
6d2e2df764
@ -262,9 +262,9 @@ sfs_readdir_common(uint64_t parent_id, uint64_t id, struct vop_readdir_args *ap,
|
||||
entry.d_fileno = id;
|
||||
entry.d_type = DT_DIR;
|
||||
entry.d_name[0] = '.';
|
||||
entry.d_name[1] = '\0';
|
||||
entry.d_namlen = 1;
|
||||
entry.d_reclen = sizeof(entry);
|
||||
dirent_terminate(&entry);
|
||||
error = vfs_read_dirent(ap, &entry, uio->uio_offset);
|
||||
if (error != 0)
|
||||
return (SET_ERROR(error));
|
||||
@ -277,9 +277,9 @@ sfs_readdir_common(uint64_t parent_id, uint64_t id, struct vop_readdir_args *ap,
|
||||
entry.d_type = DT_DIR;
|
||||
entry.d_name[0] = '.';
|
||||
entry.d_name[1] = '.';
|
||||
entry.d_name[2] = '\0';
|
||||
entry.d_namlen = 2;
|
||||
entry.d_reclen = sizeof(entry);
|
||||
dirent_terminate(&entry);
|
||||
error = vfs_read_dirent(ap, &entry, uio->uio_offset);
|
||||
if (error != 0)
|
||||
return (SET_ERROR(error));
|
||||
@ -694,6 +694,7 @@ zfsctl_root_readdir(ap)
|
||||
strcpy(entry.d_name, node->snapdir->sn_name);
|
||||
entry.d_namlen = strlen(entry.d_name);
|
||||
entry.d_reclen = sizeof(entry);
|
||||
dirent_terminate(&entry);
|
||||
error = vfs_read_dirent(ap, &entry, uio->uio_offset);
|
||||
if (error != 0) {
|
||||
if (error == ENAMETOOLONG)
|
||||
@ -1099,6 +1100,7 @@ zfsctl_snapdir_readdir(ap)
|
||||
entry.d_reclen = sizeof(entry);
|
||||
/* NOTE: d_off is the offset for the *next* entry. */
|
||||
entry.d_off = cookie + dots_offset;
|
||||
dirent_terminate(&entry);
|
||||
error = vfs_read_dirent(ap, &entry, uio->uio_offset);
|
||||
if (error != 0) {
|
||||
if (error == ENAMETOOLONG)
|
||||
|
@ -2547,6 +2547,7 @@ zfs_readdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, int *ncookies, u_lon
|
||||
next = &odp->d_off;
|
||||
(void) strlcpy(odp->d_name, zap.za_name, odp->d_namlen + 1);
|
||||
odp->d_type = type;
|
||||
dirent_terminate(odp);
|
||||
odp = (dirent64_t *)((intptr_t)odp + reclen);
|
||||
}
|
||||
outcount += reclen;
|
||||
|
@ -34,6 +34,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/dirent.h>
|
||||
@ -44,7 +45,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/namei.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <sys/tree.h>
|
||||
#include <sys/vnode.h>
|
||||
@ -354,14 +354,11 @@ autofs_readdir_one(struct uio *uio, const char *name, int fileno,
|
||||
size_t *reclenp)
|
||||
{
|
||||
struct dirent dirent;
|
||||
size_t namlen, padded_namlen, reclen;
|
||||
size_t namlen, reclen;
|
||||
int error;
|
||||
|
||||
namlen = strlen(name);
|
||||
padded_namlen = roundup2(namlen + 1, __alignof(struct dirent));
|
||||
KASSERT(padded_namlen <= MAXNAMLEN, ("%zd > MAXNAMLEN", padded_namlen));
|
||||
reclen = offsetof(struct dirent, d_name) + padded_namlen;
|
||||
|
||||
reclen = _GENERIC_DIRLEN(namlen);
|
||||
if (reclenp != NULL)
|
||||
*reclenp = reclen;
|
||||
|
||||
@ -376,7 +373,7 @@ autofs_readdir_one(struct uio *uio, const char *name, int fileno,
|
||||
dirent.d_type = DT_DIR;
|
||||
dirent.d_namlen = namlen;
|
||||
memcpy(dirent.d_name, name, namlen);
|
||||
memset(dirent.d_name + namlen, 0, padded_namlen - namlen);
|
||||
dirent_terminate(&dirent);
|
||||
error = uiomove(&dirent, reclen, uio);
|
||||
|
||||
return (error);
|
||||
|
@ -380,8 +380,8 @@ iso_uiodir(idp,dp,off)
|
||||
{
|
||||
int error;
|
||||
|
||||
dp->d_name[dp->d_namlen] = 0;
|
||||
dp->d_reclen = GENERIC_DIRSIZ(dp);
|
||||
dirent_terminate(dp);
|
||||
|
||||
if (idp->uio->uio_resid < dp->d_reclen) {
|
||||
idp->eofflag = 0;
|
||||
|
@ -226,7 +226,7 @@ devfs_newdirent(char *name, int namelen)
|
||||
de->de_dirent->d_namlen = namelen;
|
||||
de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
|
||||
bcopy(name, de->de_dirent->d_name, namelen);
|
||||
de->de_dirent->d_name[namelen] = '\0';
|
||||
dirent_terminate(de->de_dirent);
|
||||
vfs_timestamp(&de->de_ctime);
|
||||
de->de_mtime = de->de_atime = de->de_ctime;
|
||||
de->de_links = 1;
|
||||
|
@ -223,9 +223,9 @@ ext2_readdir(struct vop_readdir_args *ap)
|
||||
dstdp.d_fileno = dp->e2d_ino;
|
||||
dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
|
||||
bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen);
|
||||
dstdp.d_name[dstdp.d_namlen] = '\0';
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
dstdp.d_off = offset + dp->e2d_reclen;
|
||||
dirent_terminate(&dstdp);
|
||||
if (dstdp.d_reclen > uio->uio_resid) {
|
||||
if (uio->uio_resid == startresid)
|
||||
error = EINVAL;
|
||||
|
@ -561,8 +561,8 @@ fdesc_readdir(struct vop_readdir_args *ap)
|
||||
dp->d_namlen = i + 1;
|
||||
dp->d_reclen = UIO_MX;
|
||||
bcopy("..", dp->d_name, dp->d_namlen);
|
||||
dp->d_name[i + 1] = '\0';
|
||||
dp->d_type = DT_DIR;
|
||||
dirent_terminate(dp);
|
||||
break;
|
||||
default:
|
||||
if (fdp->fd_ofiles[fcnt].fde_file == NULL)
|
||||
@ -572,6 +572,7 @@ fdesc_readdir(struct vop_readdir_args *ap)
|
||||
dp->d_type = (fmp->flags & FMNT_LINRDLNKF) == 0 ?
|
||||
DT_CHR : DT_LNK;
|
||||
dp->d_fileno = i + FD_DESC;
|
||||
dirent_terminate(dp);
|
||||
break;
|
||||
}
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
|
@ -357,7 +357,7 @@ fuse_internal_readdir_processdata(struct uio *uio,
|
||||
memcpy((char *)cookediov->base + sizeof(struct dirent) -
|
||||
MAXNAMLEN - 1,
|
||||
(char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
|
||||
((char *)cookediov->base)[bytesavail - 1] = '\0';
|
||||
dirent_terminate(de);
|
||||
|
||||
err = uiomove(cookediov->base, cookediov->len, uio);
|
||||
if (err) {
|
||||
|
@ -1550,16 +1550,18 @@ msdosfs_readdir(struct vop_readdir_args *ap)
|
||||
switch (n) {
|
||||
case 0:
|
||||
dirbuf.d_namlen = 1;
|
||||
strcpy(dirbuf.d_name, ".");
|
||||
dirbuf.d_name[0] = '.';
|
||||
break;
|
||||
case 1:
|
||||
dirbuf.d_namlen = 2;
|
||||
strcpy(dirbuf.d_name, "..");
|
||||
dirbuf.d_name[0] = '.';
|
||||
dirbuf.d_name[1] = '.';
|
||||
break;
|
||||
}
|
||||
dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf);
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
dirbuf.d_off = offset + sizeof(struct direntry);
|
||||
dirent_terminate(&dirbuf);
|
||||
if (uio->uio_resid < dirbuf.d_reclen)
|
||||
goto out;
|
||||
error = uiomove(&dirbuf, dirbuf.d_reclen, uio);
|
||||
|
@ -1226,7 +1226,6 @@ nandfs_readdir(struct vop_readdir_args *ap)
|
||||
ndirent = (struct nandfs_dir_entry *)pos;
|
||||
|
||||
name_len = ndirent->name_len;
|
||||
memset(&dirent, 0, sizeof(struct dirent));
|
||||
dirent.d_fileno = ndirent->inode;
|
||||
if (dirent.d_fileno) {
|
||||
dirent.d_type = ndirent->file_type;
|
||||
@ -1235,6 +1234,7 @@ nandfs_readdir(struct vop_readdir_args *ap)
|
||||
dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
dirent.d_off = diroffset + ndirent->rec_len;
|
||||
dirent_terminate(&dirent);
|
||||
DPRINTF(READDIR, ("copying `%*.*s`\n", name_len,
|
||||
name_len, dirent.d_name));
|
||||
}
|
||||
@ -1243,12 +1243,12 @@ nandfs_readdir(struct vop_readdir_args *ap)
|
||||
* If there isn't enough space in the uio to return a
|
||||
* whole dirent, break off read
|
||||
*/
|
||||
if (uio->uio_resid < GENERIC_DIRSIZ(&dirent))
|
||||
if (uio->uio_resid < dirent.d_reclen)
|
||||
break;
|
||||
|
||||
/* Transfer */
|
||||
if (dirent.d_fileno)
|
||||
uiomove(&dirent, GENERIC_DIRSIZ(&dirent), uio);
|
||||
uiomove(&dirent, dirent.d_reclen, uio);
|
||||
|
||||
/* Advance */
|
||||
diroffset += ndirent->rec_len;
|
||||
|
@ -828,7 +828,6 @@ pfs_readdir(struct vop_readdir_args *va)
|
||||
/* PFS_DELEN was picked to fit PFS_NAMLEN */
|
||||
for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
|
||||
pfsent->entry.d_name[i] = pn->pn_name[i];
|
||||
pfsent->entry.d_name[i] = 0;
|
||||
pfsent->entry.d_namlen = i;
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
pfsent->entry.d_off = offset + PFS_DELEN;
|
||||
@ -855,6 +854,7 @@ pfs_readdir(struct vop_readdir_args *va)
|
||||
panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
|
||||
}
|
||||
PFS_TRACE(("%s", pfsent->entry.d_name));
|
||||
dirent_terminate(&pfsent->entry);
|
||||
STAILQ_INSERT_TAIL(&lst, pfsent, link);
|
||||
offset += PFS_DELEN;
|
||||
resid -= PFS_DELEN;
|
||||
|
@ -106,8 +106,8 @@ smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
|
||||
de.d_namlen = offset + 1;
|
||||
de.d_name[0] = '.';
|
||||
de.d_name[1] = '.';
|
||||
de.d_name[offset + 1] = '\0';
|
||||
de.d_type = DT_DIR;
|
||||
dirent_terminate(&de);
|
||||
error = uiomove(&de, DE_SIZE, uio);
|
||||
if (error)
|
||||
goto out;
|
||||
@ -156,7 +156,7 @@ smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
|
||||
de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG;
|
||||
de.d_namlen = ctx->f_nmlen;
|
||||
bcopy(ctx->f_name, de.d_name, de.d_namlen);
|
||||
de.d_name[de.d_namlen] = '\0';
|
||||
dirent_terminate(&de);
|
||||
if (smbfs_fastlookup) {
|
||||
error = smbfs_nget(vp->v_mount, vp, ctx->f_name,
|
||||
ctx->f_nmlen, &ctx->f_attr, &newvp);
|
||||
|
@ -39,6 +39,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/fnv_hash.h>
|
||||
#include <sys/lock.h>
|
||||
@ -50,7 +51,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/random.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/vmmeter.h>
|
||||
@ -1120,8 +1120,8 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio)
|
||||
dent.d_type = DT_DIR;
|
||||
dent.d_namlen = 1;
|
||||
dent.d_name[0] = '.';
|
||||
dent.d_name[1] = '\0';
|
||||
dent.d_reclen = GENERIC_DIRSIZ(&dent);
|
||||
dirent_terminate(&dent);
|
||||
|
||||
if (dent.d_reclen > uio->uio_resid)
|
||||
error = EJUSTRETURN;
|
||||
@ -1164,8 +1164,8 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio)
|
||||
dent.d_namlen = 2;
|
||||
dent.d_name[0] = '.';
|
||||
dent.d_name[1] = '.';
|
||||
dent.d_name[2] = '\0';
|
||||
dent.d_reclen = GENERIC_DIRSIZ(&dent);
|
||||
dirent_terminate(&dent);
|
||||
|
||||
if (dent.d_reclen > uio->uio_resid)
|
||||
error = EJUSTRETURN;
|
||||
@ -1285,8 +1285,8 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, int maxcookies,
|
||||
d.d_namlen = de->td_namelen;
|
||||
MPASS(de->td_namelen < sizeof(d.d_name));
|
||||
(void)memcpy(d.d_name, de->ud.td_name, de->td_namelen);
|
||||
d.d_name[de->td_namelen] = '\0';
|
||||
d.d_reclen = GENERIC_DIRSIZ(&d);
|
||||
dirent_terminate(&d);
|
||||
|
||||
/* Stop reading if the directory entry we are treating is
|
||||
* bigger than the amount of data that can be returned. */
|
||||
|
@ -46,6 +46,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/lock.h>
|
||||
@ -56,7 +57,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/limits.h>
|
||||
@ -51,7 +52,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/sched.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/vnode.h>
|
||||
|
@ -843,10 +843,10 @@ udf_readdir(struct vop_readdir_args *a)
|
||||
dir.d_fileno = node->hash_id;
|
||||
dir.d_type = DT_DIR;
|
||||
dir.d_name[0] = '.';
|
||||
dir.d_name[1] = '\0';
|
||||
dir.d_namlen = 1;
|
||||
dir.d_reclen = GENERIC_DIRSIZ(&dir);
|
||||
dir.d_off = 1;
|
||||
dirent_terminate(&dir);
|
||||
uiodir.dirent = &dir;
|
||||
error = udf_uiodir(&uiodir, dir.d_reclen, uio, 1);
|
||||
if (error)
|
||||
@ -856,10 +856,10 @@ udf_readdir(struct vop_readdir_args *a)
|
||||
dir.d_type = DT_DIR;
|
||||
dir.d_name[0] = '.';
|
||||
dir.d_name[1] = '.';
|
||||
dir.d_name[2] = '\0';
|
||||
dir.d_namlen = 2;
|
||||
dir.d_reclen = GENERIC_DIRSIZ(&dir);
|
||||
dir.d_off = 2;
|
||||
dirent_terminate(&dir);
|
||||
uiodir.dirent = &dir;
|
||||
error = udf_uiodir(&uiodir, dir.d_reclen, uio, 2);
|
||||
} else {
|
||||
@ -870,6 +870,7 @@ udf_readdir(struct vop_readdir_args *a)
|
||||
DT_DIR : DT_UNKNOWN;
|
||||
dir.d_reclen = GENERIC_DIRSIZ(&dir);
|
||||
dir.d_off = ds->this_off;
|
||||
dirent_terminate(&dir);
|
||||
uiodir.dirent = &dir;
|
||||
error = udf_uiodir(&uiodir, dir.d_reclen, uio,
|
||||
ds->this_off);
|
||||
|
@ -1428,7 +1428,6 @@ mqfs_readdir(struct vop_readdir_args *ap)
|
||||
entry.d_fileno = pn->mn_fileno;
|
||||
for (i = 0; i < MQFS_NAMELEN - 1 && pn->mn_name[i] != '\0'; ++i)
|
||||
entry.d_name[i] = pn->mn_name[i];
|
||||
entry.d_name[i] = 0;
|
||||
entry.d_namlen = i;
|
||||
switch (pn->mn_type) {
|
||||
case mqfstype_root:
|
||||
@ -1447,6 +1446,7 @@ mqfs_readdir(struct vop_readdir_args *ap)
|
||||
panic("%s has unexpected node type: %d", pn->mn_name,
|
||||
pn->mn_type);
|
||||
}
|
||||
dirent_terminate(&entry);
|
||||
if (entry.d_reclen > uio->uio_resid)
|
||||
break;
|
||||
if (offset >= uio->uio_offset) {
|
||||
|
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "opt_inet6.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/jail.h>
|
||||
#include <sys/kernel.h>
|
||||
@ -55,7 +56,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/refcount.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
@ -126,6 +126,19 @@ struct freebsd11_dirent {
|
||||
|
||||
#ifdef _KERNEL
|
||||
#define GENERIC_DIRSIZ(dp) _GENERIC_DIRSIZ(dp)
|
||||
|
||||
/*
|
||||
* Ensure that padding bytes are zeroed and that the name is NUL-terminated.
|
||||
*/
|
||||
static inline void
|
||||
dirent_terminate(struct dirent *dp)
|
||||
{
|
||||
|
||||
dp->d_pad0 = 0;
|
||||
dp->d_pad1 = 0;
|
||||
memset(dp->d_name + dp->d_namlen, 0,
|
||||
dp->d_reclen - (__offsetof(struct dirent, d_name) + dp->d_namlen));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_SYS_DIRENT_H_ */
|
||||
|
@ -2217,9 +2217,9 @@ ufs_readdir(ap)
|
||||
dstdp.d_fileno = dp->d_ino;
|
||||
dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
|
||||
bcopy(dp->d_name, dstdp.d_name, dstdp.d_namlen);
|
||||
dstdp.d_name[dstdp.d_namlen] = '\0';
|
||||
/* NOTE: d_off is the offset of the *next* entry. */
|
||||
dstdp.d_off = offset + dp->d_reclen;
|
||||
dirent_terminate(&dstdp);
|
||||
if (dstdp.d_reclen > uio->uio_resid) {
|
||||
if (uio->uio_resid == startresid)
|
||||
error = EINVAL;
|
||||
|
Loading…
Reference in New Issue
Block a user