| Avoid leaking kernel stack when creating directory names. |
| From FreeBSD base r347475 (MFC r347066). |
| |
| --- stable/11/sys/ufs/ufs/dir.h 2019/05/10 23:45:16 347474 |
| +++ stable/11/sys/ufs/ufs/dir.h 2019/05/10 23:46:42 347475 |
| @@ -105,13 +105,11 @@ |
| * The DIRSIZ macro gives the minimum record length which will hold |
| * the directory entry. This requires the amount of space in struct direct |
| * without the d_name field, plus enough space for the name with a terminating |
| - * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. |
| - * |
| - * |
| + * null byte (dp->d_namlen + 1), rounded up to a 4 byte boundary. |
| */ |
| -#define DIRECTSIZ(namlen) \ |
| - ((__offsetof(struct direct, d_name) + \ |
| - ((namlen)+1)*sizeof(((struct direct *)0)->d_name[0]) + 3) & ~3) |
| +#define DIR_ROUNDUP 4 /* Directory name roundup size */ |
| +#define DIRECTSIZ(namlen) \ |
| + (roundup2(__offsetof(struct direct, d_name) + (namlen) + 1, DIR_ROUNDUP)) |
| #if (BYTE_ORDER == LITTLE_ENDIAN) |
| #define DIRSIZ(oldfmt, dp) \ |
| ((oldfmt) ? DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) |
| --- stable/11/sys/ufs/ufs/ufs_lookup.c 2018/03/29 02:50:57 331722 |
| +++ stable/11/sys/ufs/ufs/ufs_lookup.c 2019/05/10 23:46:42 347475 |
| @@ -823,14 +823,21 @@ |
| struct componentname *cnp; |
| struct direct *newdirp; |
| { |
| + u_int namelen; |
| |
| -#ifdef INVARIANTS |
| - if ((cnp->cn_flags & SAVENAME) == 0) |
| - panic("ufs_makedirentry: missing name"); |
| -#endif |
| + namelen = (unsigned)cnp->cn_namelen; |
| + KASSERT((cnp->cn_flags & SAVENAME) != 0, |
| + ("ufs_makedirentry: missing name")); |
| + KASSERT(namelen <= MAXNAMLEN, |
| + ("ufs_makedirentry: name too long")); |
| newdirp->d_ino = ip->i_number; |
| - newdirp->d_namlen = cnp->cn_namelen; |
| - bcopy(cnp->cn_nameptr, newdirp->d_name, (unsigned)cnp->cn_namelen + 1); |
| + newdirp->d_namlen = namelen; |
| + |
| + /* Zero out after-name padding */ |
| + *(u_int32_t *)(&newdirp->d_name[namelen & ~(DIR_ROUNDUP - 1)]) = 0; |
| + |
| + bcopy(cnp->cn_nameptr, newdirp->d_name, namelen); |
| + |
| if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) |
| newdirp->d_type = IFTODT(ip->i_mode); |
| else { |
| @@ -1209,16 +1216,21 @@ |
| if (ip && rep->d_ino != ip->i_number) |
| panic("ufs_dirremove: ip %ju does not match dirent ino %ju\n", |
| (uintmax_t)ip->i_number, (uintmax_t)rep->d_ino); |
| - if (dp->i_count == 0) { |
| - /* |
| - * First entry in block: set d_ino to zero. |
| - */ |
| - ep->d_ino = 0; |
| - } else { |
| + /* |
| + * Zero out the file directory entry metadata to reduce disk |
| + * scavenging disclosure. |
| + */ |
| + bzero(&rep->d_name[0], rep->d_namlen); |
| + rep->d_namlen = 0; |
| + rep->d_type = 0; |
| + rep->d_ino = 0; |
| + |
| + if (dp->i_count != 0) { |
| /* |
| * Collapse new free space into previous entry. |
| */ |
| ep->d_reclen += rep->d_reclen; |
| + rep->d_reclen = 0; |
| } |
| #ifdef UFS_DIRHASH |
| if (dp->i_dirhash != NULL) |