aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs
diff options
context:
space:
mode:
authorJessica Clarke <jrtc27@FreeBSD.org>2022-01-02 20:55:36 +0000
committerJessica Clarke <jrtc27@FreeBSD.org>2022-01-02 20:55:36 +0000
commit5b13fa7987c13aa7b5a67cc6b465475912de2d14 (patch)
tree0b92d95488bae37aa8703134a1b6696ba9e041cf /sys/ufs
parent04fd468da0d0baea535da418b92df74101a9659d (diff)
downloadsrc-5b13fa7987c13aa7b5a67cc6b465475912de2d14.tar.gz
src-5b13fa7987c13aa7b5a67cc6b465475912de2d14.zip
ufs: Rework shortlink handling to avoid subobject overflows
Shortlinks occupy the space of both di_db and di_ib when used. However, everywhere that wants to read or write a shortlink takes a pointer do di_db and promptly runs off the end of it into di_ib. This is fine on most architectures, if a little dodgy. However, on CHERI, the compiler can optionally restrict the bounds on pointers to subobjects to just that subobject, in order to mitigate intra-object buffer overflows, and this is enabled in CheriBSD's pure-capability kernels. Instead, clean this up by inserting a union such that a new di_shortlink can be added with the right size and element type, avoiding the need to cast and allowing the use of the DIP macro to access the field. This also mirrors how the ext2fs code implements extents support, with the exact same structure other than having a uint32_t i_data[] instead of a char di_shortlink[]. Reviewed by: mckusick, jhb Differential Revision: https://reviews.freebsd.org/D33650
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_inode.c2
-rw-r--r--sys/ufs/ufs/dinode.h24
-rw-r--r--sys/ufs/ufs/inode.h2
-rw-r--r--sys/ufs/ufs/ufs_vnops.c4
4 files changed, 23 insertions, 9 deletions
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c
index 4b31b4febcbd..d8b86df27121 100644
--- a/sys/ufs/ffs/ffs_inode.c
+++ b/sys/ufs/ffs/ffs_inode.c
@@ -342,7 +342,7 @@ ffs_truncate(vp, length, flags, cred)
if (length != 0)
panic("ffs_truncate: partial truncate of symlink");
#endif
- bzero(SHORTLINK(ip), (u_int)ip->i_size);
+ bzero(DIP(ip, i_shortlink), (u_int)ip->i_size);
ip->i_size = 0;
DIP_SET(ip, i_size, 0);
UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE);
diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h
index 1f0f25c4d5ec..840a4cc7d40f 100644
--- a/sys/ufs/ufs/dinode.h
+++ b/sys/ufs/ufs/dinode.h
@@ -145,8 +145,16 @@ struct ufs2_dinode {
u_int32_t di_flags; /* 88: Status flags (chflags). */
u_int32_t di_extsize; /* 92: External attributes size. */
ufs2_daddr_t di_extb[UFS_NXADDR];/* 96: External attributes block. */
- ufs2_daddr_t di_db[UFS_NDADDR]; /* 112: Direct disk blocks. */
- ufs2_daddr_t di_ib[UFS_NIADDR]; /* 208: Indirect disk blocks. */
+ union {
+ struct {
+ ufs2_daddr_t di_db /* 112: Direct disk blocks. */
+ [UFS_NDADDR];
+ ufs2_daddr_t di_ib /* 208: Indirect disk blocks. */
+ [UFS_NIADDR];
+ };
+ char di_shortlink /* 112: Embedded symbolic link. */
+ [(UFS_NDADDR + UFS_NIADDR) * sizeof(ufs2_daddr_t)];
+ };
u_int64_t di_modrev; /* 232: i_modrev for NFSv4 */
uint32_t di_freelink; /* 240: SUJ: Next unlinked inode. */
uint32_t di_ckhash; /* 244: if CK_INODE, its check-hash */
@@ -179,8 +187,16 @@ struct ufs1_dinode {
int32_t di_mtimensec; /* 28: Last modified time. */
int32_t di_ctime; /* 32: Last inode change time. */
int32_t di_ctimensec; /* 36: Last inode change time. */
- ufs1_daddr_t di_db[UFS_NDADDR]; /* 40: Direct disk blocks. */
- ufs1_daddr_t di_ib[UFS_NIADDR]; /* 88: Indirect disk blocks. */
+ union {
+ struct {
+ ufs1_daddr_t di_db /* 40: Direct disk blocks. */
+ [UFS_NDADDR];
+ ufs1_daddr_t di_ib /* 88: Indirect disk blocks. */
+ [UFS_NIADDR];
+ };
+ char di_shortlink /* 40: Embedded symbolic link. */
+ [(UFS_NDADDR + UFS_NIADDR) * sizeof(ufs1_daddr_t)];
+ };
u_int32_t di_flags; /* 100: Status flags (chflags). */
u_int32_t di_blocks; /* 104: Blocks actually held. */
u_int32_t di_gen; /* 108: Generation number. */
diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h
index a82b4658c0af..7ea88d15b1e3 100644
--- a/sys/ufs/ufs/inode.h
+++ b/sys/ufs/ufs/inode.h
@@ -246,8 +246,6 @@ I_IS_UFS2(const struct inode *ip)
(ip)->i_din2->d##field = (val); \
} while (0)
-#define SHORTLINK(ip) (I_IS_UFS1(ip) ? \
- (caddr_t)(ip)->i_din1->di_db : (caddr_t)(ip)->i_din2->di_db)
#define IS_SNAPSHOT(ip) ((ip)->i_flags & SF_SNAPSHOT)
/*
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 89454d0effff..1d0525bcbe0c 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -2311,7 +2311,7 @@ ufs_symlink(ap)
len = strlen(ap->a_target);
if (len < VFSTOUFS(vp->v_mount)->um_maxsymlinklen) {
ip = VTOI(vp);
- bcopy(ap->a_target, SHORTLINK(ip), len);
+ bcopy(ap->a_target, DIP(ip, i_shortlink), len);
ip->i_size = len;
DIP_SET(ip, i_size, len);
UFS_INODE_SET_FLAG(ip, IN_SIZEMOD | IN_CHANGE | IN_UPDATE);
@@ -2481,7 +2481,7 @@ ufs_readlink(ap)
isize = ip->i_size;
if (isize < VFSTOUFS(vp->v_mount)->um_maxsymlinklen)
- return (uiomove(SHORTLINK(ip), isize, ap->a_uio));
+ return (uiomove(DIP(ip, i_shortlink), isize, ap->a_uio));
return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
}