Skip to content

Commit

Permalink
Merge tag 'afs-fixes-04012021' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
torvalds committed Jan 5, 2021
2 parents c2407cf + 366911c commit 6207214
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 31 deletions.
49 changes: 26 additions & 23 deletions fs/afs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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)",
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)",
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);
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 */
Expand Down
6 changes: 2 additions & 4 deletions fs/afs/dir_edit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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];
Expand Down
25 changes: 21 additions & 4 deletions fs/afs/xdr_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
2 changes: 2 additions & 0 deletions include/trace/events/afs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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") \
Expand Down

0 comments on commit 6207214

Please sign in to comment.