Commit 6207214a authored by Linus Torvalds's avatar Linus Torvalds

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
parents c2407cf7 366911cd
...@@ -350,7 +350,7 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, ...@@ -350,7 +350,7 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
unsigned blkoff) unsigned blkoff)
{ {
union afs_xdr_dirent *dire; union afs_xdr_dirent *dire;
unsigned offset, next, curr; unsigned offset, next, curr, nr_slots;
size_t nlen; size_t nlen;
int tmp; int tmp;
...@@ -363,13 +363,12 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, ...@@ -363,13 +363,12 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
offset < AFS_DIR_SLOTS_PER_BLOCK; offset < AFS_DIR_SLOTS_PER_BLOCK;
offset = next offset = next
) { ) {
next = offset + 1;
/* skip entries marked unused in the bitmap */ /* skip entries marked unused in the bitmap */
if (!(block->hdr.bitmap[offset / 8] & if (!(block->hdr.bitmap[offset / 8] &
(1 << (offset % 8)))) { (1 << (offset % 8)))) {
_debug("ENT[%zu.%u]: unused", _debug("ENT[%zu.%u]: unused",
blkoff / sizeof(union afs_xdr_dir_block), offset); blkoff / sizeof(union afs_xdr_dir_block), offset);
next = offset + 1;
if (offset >= curr) if (offset >= curr)
ctx->pos = blkoff + ctx->pos = blkoff +
next * sizeof(union afs_xdr_dirent); next * sizeof(union afs_xdr_dirent);
...@@ -381,35 +380,39 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode, ...@@ -381,35 +380,39 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
nlen = strnlen(dire->u.name, nlen = strnlen(dire->u.name,
sizeof(*block) - sizeof(*block) -
offset * sizeof(union afs_xdr_dirent)); 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\"", _debug("ENT[%zu.%u]: %s %zu \"%s\"",
blkoff / sizeof(union afs_xdr_dir_block), offset, blkoff / sizeof(union afs_xdr_dir_block), offset,
(offset < curr ? "skip" : "fill"), (offset < curr ? "skip" : "fill"),
nlen, dire->u.name); nlen, dire->u.name);
/* work out where the next possible entry is */ nr_slots = afs_dir_calc_slots(nlen);
for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_xdr_dirent)) { next = offset + nr_slots;
if (next >= AFS_DIR_SLOTS_PER_BLOCK) { if (next > AFS_DIR_SLOTS_PER_BLOCK) {
_debug("ENT[%zu.%u]:" _debug("ENT[%zu.%u]:"
" %u travelled beyond end dir block" " %u extends beyond end dir block"
" (len %u/%zu)", " (len %zu)",
blkoff / sizeof(union afs_xdr_dir_block), blkoff / sizeof(union afs_xdr_dir_block),
offset, next, tmp, nlen); offset, next, nlen);
return afs_bad(dvnode, afs_file_error_dir_over_end); return afs_bad(dvnode, afs_file_error_dir_over_end);
} }
if (!(block->hdr.bitmap[next / 8] &
(1 << (next % 8)))) { /* Check that the name-extension dirents are all allocated */
_debug("ENT[%zu.%u]:" for (tmp = 1; tmp < nr_slots; tmp++) {
" %u unmarked extension (len %u/%zu)", 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), 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); 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 */ /* skip if starts before the current position */
......
...@@ -215,8 +215,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode, ...@@ -215,8 +215,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
} }
/* Work out how many slots we're going to need. */ /* 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_calc_slots(name->len);
need_slots /= AFS_DIR_DIRENT_SIZE;
meta_page = kmap(page0); meta_page = kmap(page0);
meta = &meta_page->blocks[0]; meta = &meta_page->blocks[0];
...@@ -393,8 +392,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, ...@@ -393,8 +392,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
} }
/* Work out how many slots we're going to discard. */ /* 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_calc_slots(name->len);
need_slots /= AFS_DIR_DIRENT_SIZE;
meta_page = kmap(page0); meta_page = kmap(page0);
meta = &meta_page->blocks[0]; meta = &meta_page->blocks[0];
......
...@@ -54,10 +54,16 @@ union afs_xdr_dirent { ...@@ -54,10 +54,16 @@ union afs_xdr_dirent {
__be16 hash_next; __be16 hash_next;
__be32 vnode; __be32 vnode;
__be32 unique; __be32 unique;
u8 name[16]; u8 name[];
u8 overflow[4]; /* if any char of the name (inc /* When determining the number of dirent slots needed to
* NUL) reaches here, consume * represent a directory entry, name should be assumed to be 16
* the next dirent too */ * 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; } u;
u8 extended_name[32]; u8 extended_name[32];
} __packed; } __packed;
...@@ -96,4 +102,15 @@ struct afs_xdr_dir_page { ...@@ -96,4 +102,15 @@ struct afs_xdr_dir_page {
union afs_xdr_dir_block blocks[AFS_DIR_BLOCKS_PER_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 */ #endif /* XDR_FS_H */
...@@ -231,6 +231,7 @@ enum afs_file_error { ...@@ -231,6 +231,7 @@ enum afs_file_error {
afs_file_error_dir_bad_magic, afs_file_error_dir_bad_magic,
afs_file_error_dir_big, afs_file_error_dir_big,
afs_file_error_dir_missing_page, afs_file_error_dir_missing_page,
afs_file_error_dir_name_too_long,
afs_file_error_dir_over_end, afs_file_error_dir_over_end,
afs_file_error_dir_small, afs_file_error_dir_small,
afs_file_error_dir_unmarked_ext, afs_file_error_dir_unmarked_ext,
...@@ -488,6 +489,7 @@ enum afs_cb_break_reason { ...@@ -488,6 +489,7 @@ enum afs_cb_break_reason {
EM(afs_file_error_dir_bad_magic, "DIR_BAD_MAGIC") \ EM(afs_file_error_dir_bad_magic, "DIR_BAD_MAGIC") \
EM(afs_file_error_dir_big, "DIR_BIG") \ EM(afs_file_error_dir_big, "DIR_BIG") \
EM(afs_file_error_dir_missing_page, "DIR_MISSING_PAGE") \ 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_over_end, "DIR_ENT_OVER_END") \
EM(afs_file_error_dir_small, "DIR_SMALL") \ EM(afs_file_error_dir_small, "DIR_SMALL") \
EM(afs_file_error_dir_unmarked_ext, "DIR_UNMARKED_EXT") \ EM(afs_file_error_dir_unmarked_ext, "DIR_UNMARKED_EXT") \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment