AFS fixes

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAl/znRUACgkQ+7dXa6fL
 C2tCgQ/9Erue3NZlcqHxYtpEUWniUUS39zRmGaPnDvnPqykM4nqN196udMPzeVx1
 U/Uc1y4bQqLy43aPKVqpOMtjsiQfdqRTc1S8iZwd+4tv39R0RuBn2fP6d52cbsj1
 plCS2qHr9YHic+PPlOOZ21uU2hfDwyZwEcbmhaA0TrFq3nLkNqcP1nRKKEtTxhUD
 EG0j2LRDKrBDDWKqo/lwNb81lnyTJsbnuFVWVI11UK1+xjyIkCBc7KZg4kMH+6+r
 Sc7De1qe0w3qYczLKXfKvi4axRkXIKaswvwlEA1+HDXK1HwYuoVgbfnF/Z3ILA5s
 sr4dNx/M+M9CB5OaUG4rnn1VUQwhFK7c2MftR8iaXiYReK1rjV3p+AQ0B8OUDxb7
 un8w0gIdDaaw7qN52iaywXWf+pYKDIqE5IHEm/daNNPfmC9S4vMLLc89tTzvi1aC
 YGH6+dERUtf5lnR2L9z3GodMDuEW3YpLelgVspL7tGYsQm3nvsiIi1vew/QR4qir
 TksjJOwu/0WhHAJD32rnmppiJQ+s5a1N+vo46Ax69fV9YAglNWPPYYUZyY95+mw/
 g5Y8jswCy0h89yq/3nYBMJsgFhFY2LNOOn+1C9PtQxKcXkSubVLh7ssw0aORD52q
 UbdPnG5RoU7UpqmvGnosWIoO+iYAKp23DCIiJLIoR9Ve0RfAx3Q=
 =OQRI
 -----END PGP SIGNATURE-----

Merge tag 'afs-fixes-04012021' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:
 "Two fixes.

  The first is the fix for the strnlen() array limit check and the
  second fixes the calculation of the number of dirent records used to
  represent any particular filename length"

* tag 'afs-fixes-04012021' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix directory entry size calculation
  afs: Work around strnlen() oops with CONFIG_FORTIFIED_SOURCE=y
This commit is contained in:
Linus Torvalds 2021-01-05 11:55:46 -08:00
commit 6207214a70
4 changed files with 51 additions and 31 deletions

View file

@ -350,7 +350,7 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
unsigned blkoff)
{
union afs_xdr_dirent *dire;
unsigned offset, next, curr;
unsigned offset, next, curr, nr_slots;
size_t nlen;
int tmp;
@ -363,13 +363,12 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
offset < AFS_DIR_SLOTS_PER_BLOCK;
offset = next
) {
next = offset + 1;
/* skip entries marked unused in the bitmap */
if (!(block->hdr.bitmap[offset / 8] &
(1 << (offset % 8)))) {
_debug("ENT[%zu.%u]: unused",
blkoff / sizeof(union afs_xdr_dir_block), offset);
next = offset + 1;
if (offset >= curr)
ctx->pos = blkoff +
next * sizeof(union afs_xdr_dirent);
@ -381,35 +380,39 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
nlen = strnlen(dire->u.name,
sizeof(*block) -
offset * sizeof(union afs_xdr_dirent));
if (nlen > AFSNAMEMAX - 1) {
_debug("ENT[%zu]: name too long (len %u/%zu)",
blkoff / sizeof(union afs_xdr_dir_block),
offset, nlen);
return afs_bad(dvnode, afs_file_error_dir_name_too_long);
}
_debug("ENT[%zu.%u]: %s %zu \"%s\"",
blkoff / sizeof(union afs_xdr_dir_block), offset,
(offset < curr ? "skip" : "fill"),
nlen, dire->u.name);
/* work out where the next possible entry is */
for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_xdr_dirent)) {
if (next >= AFS_DIR_SLOTS_PER_BLOCK) {
_debug("ENT[%zu.%u]:"
" %u travelled beyond end dir block"
" (len %u/%zu)",
nr_slots = afs_dir_calc_slots(nlen);
next = offset + nr_slots;
if (next > AFS_DIR_SLOTS_PER_BLOCK) {
_debug("ENT[%zu.%u]:"
" %u extends beyond end dir block"
" (len %zu)",
blkoff / sizeof(union afs_xdr_dir_block),
offset, next, nlen);
return afs_bad(dvnode, afs_file_error_dir_over_end);
}
/* Check that the name-extension dirents are all allocated */
for (tmp = 1; tmp < nr_slots; tmp++) {
unsigned int ix = offset + tmp;
if (!(block->hdr.bitmap[ix / 8] & (1 << (ix % 8)))) {
_debug("ENT[%zu.u]:"
" %u unmarked extension (%u/%u)",
blkoff / sizeof(union afs_xdr_dir_block),
offset, next, tmp, nlen);
return afs_bad(dvnode, afs_file_error_dir_over_end);
}
if (!(block->hdr.bitmap[next / 8] &
(1 << (next % 8)))) {
_debug("ENT[%zu.%u]:"
" %u unmarked extension (len %u/%zu)",
blkoff / sizeof(union afs_xdr_dir_block),
offset, next, tmp, nlen);
offset, tmp, nr_slots);
return afs_bad(dvnode, afs_file_error_dir_unmarked_ext);
}
_debug("ENT[%zu.%u]: ext %u/%zu",
blkoff / sizeof(union afs_xdr_dir_block),
next, tmp, nlen);
next++;
}
/* skip if starts before the current position */

View file

@ -215,8 +215,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
}
/* Work out how many slots we're going to need. */
need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
need_slots /= AFS_DIR_DIRENT_SIZE;
need_slots = afs_dir_calc_slots(name->len);
meta_page = kmap(page0);
meta = &meta_page->blocks[0];
@ -393,8 +392,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
}
/* Work out how many slots we're going to discard. */
need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
need_slots /= AFS_DIR_DIRENT_SIZE;
need_slots = afs_dir_calc_slots(name->len);
meta_page = kmap(page0);
meta = &meta_page->blocks[0];

View file

@ -54,10 +54,16 @@ union afs_xdr_dirent {
__be16 hash_next;
__be32 vnode;
__be32 unique;
u8 name[16];
u8 overflow[4]; /* if any char of the name (inc
* NUL) reaches here, consume
* the next dirent too */
u8 name[];
/* When determining the number of dirent slots needed to
* represent a directory entry, name should be assumed to be 16
* bytes, due to a now-standardised (mis)calculation, but it is
* in fact 20 bytes in size. afs_dir_calc_slots() should be
* used for this.
*
* For names longer than (16 or) 20 bytes, extra slots should
* be annexed to this one using the extended_name format.
*/
} u;
u8 extended_name[32];
} __packed;
@ -96,4 +102,15 @@ struct afs_xdr_dir_page {
union afs_xdr_dir_block blocks[AFS_DIR_BLOCKS_PER_PAGE];
};
/*
* Calculate the number of dirent slots required for any given name length.
* The calculation is made assuming the part of the name in the first slot is
* 16 bytes, rather than 20, but this miscalculation is now standardised.
*/
static inline unsigned int afs_dir_calc_slots(size_t name_len)
{
name_len++; /* NUL-terminated */
return 1 + ((name_len + 15) / AFS_DIR_DIRENT_SIZE);
}
#endif /* XDR_FS_H */

View file

@ -231,6 +231,7 @@ enum afs_file_error {
afs_file_error_dir_bad_magic,
afs_file_error_dir_big,
afs_file_error_dir_missing_page,
afs_file_error_dir_name_too_long,
afs_file_error_dir_over_end,
afs_file_error_dir_small,
afs_file_error_dir_unmarked_ext,
@ -488,6 +489,7 @@ enum afs_cb_break_reason {
EM(afs_file_error_dir_bad_magic, "DIR_BAD_MAGIC") \
EM(afs_file_error_dir_big, "DIR_BIG") \
EM(afs_file_error_dir_missing_page, "DIR_MISSING_PAGE") \
EM(afs_file_error_dir_name_too_long, "DIR_NAME_TOO_LONG") \
EM(afs_file_error_dir_over_end, "DIR_ENT_OVER_END") \
EM(afs_file_error_dir_small, "DIR_SMALL") \
EM(afs_file_error_dir_unmarked_ext, "DIR_UNMARKED_EXT") \