new file mode 100755
@@ -0,0 +1,96 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Huawei Limited. All Rights Reserved.
+#
+# FS QA Test No. 554
+#
+# Check the running state of the XFS under illegal bestfree count
+# for leaf directory format.
+
+. ./common/preamble
+_begin_fstest auto quick dangerous
+
+# Import common functions.
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+
+# Remove all entries in the last logic dir data block
+leaf_dir_remove_lastdb_entries()
+{
+ local dir_ino=$1
+ local blks_pdirb=$2
+ local nblocks=$(_scratch_xfs_get_metadata_field 'core.nblocks' "inode ${dir_ino}")
+ #last dir data block index = total blocks - one leaf dir block - one dir block
+ local last_db=$((nblocks - blks_pdirb * 2))
+
+ echo "last dir block $last_db" >> $seqres.full
+ _scratch_xfs_db -c "inode $dir_ino" -c "dblock ${last_db}" -c 'p du' |\
+ grep ".name =" | sed -e 's/^.*.name = //g' \
+ -e 's/\"//g' > $tmp.ldb_ents ||\
+ _fail "get last dir block entries failed"
+ cat $tmp.ldb_ents >> $seqres.full
+ _scratch_mount
+ cat $tmp.ldb_ents | while read f
+ do
+ rm -rf ${SCRATCH_MNT}/S_IFDIR.FMT_LEAF/$f
+ done
+ _scratch_unmount
+}
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount
+
+echo "Create and check leaf dir"
+blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
+dirblksz=`$XFS_INFO_PROG "${SCRATCH_DEV}" | grep naming.*bsize |
+ sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g'`
+
+# Usually, following routine will create a directory with one leaf block
+# and three data block, meanwhile, the last data block is not full.
+__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF" "$((dirblksz / 12))"
+leaf_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF")"
+_scratch_unmount
+
+# Delete directory entries in the last directory block,
+leaf_dir_remove_lastdb_entries ${leaf_dir} $((dirblksz / blksz))
+
+# Check leaf directory
+leaf_lblk="$((32 * 1073741824 / blksz))"
+node_lblk="$((64 * 1073741824 / blksz))"
+__populate_check_xfs_dir "${leaf_dir}" "leaf"
+
+# Inject abnormal bestfree count
+echo "Inject bad bestfree count."
+_scratch_xfs_db -x -c "inode ${leaf_dir}" -c "dblock ${leaf_lblk}" \
+ -c "write ltail.bestcount 0"
+
+# Adding new entries to S_IFDIR.FMT_LEAF. Since we delete the files
+# in last directory block, current dir block have no spare space for new
+# entry. With ltail.bestcount setting illegally (eg. bestcount=0), then
+# creating new directories, which will trigger xfs to allocate new dir
+# block, meanwhile, exception will be triggered.
+# Root cause is that xfs don't examin the number bestfree slots, when the
+# slots number less than dir data blocks, if we need to allocate new dir
+# data block and update the bestfree array, we will use the dir block number
+# as index to assign bestfree array, while we did not check the leaf buf
+# boundary which may cause UAF or other memory access problem.
+echo "Add directory entries to trigger exception."
+_scratch_mount
+seq 1 $((dirblksz / 24)) | while read d
+do
+ mkdir "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF/TEST$(printf "%.04d" "$d")" >> $seqres.full 2>&1
+done
+_scratch_unmount
+
+# Bad bestfree count should be found and fixed by xfs_repair
+_scratch_xfs_repair -n >> $seqres.full 2>&1
+egrep -q 'leaf block.*bad tail' $seqres.full && echo "Repair found problems."
+_repair_scratch_fs >> $seqres.full 2>&1 || _fail "Repair failed!"
+
+# Success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,7 @@
+QA output created by 554
+Format and mount
+Create and check leaf dir
+Inject bad bestfree count.
+ltail.bestcount = 0
+Add directory entries to trigger exception.
+Repair found problems.
Test leaf dir allocting new block when bestfree array size less than data blocks count, which may lead to UAF. Signed-off-by: Guo Xuenan <guoxuenan@huawei.com> --- tests/xfs/554 | 96 +++++++++++++++++++++++++++++++++++++++++++++++ tests/xfs/554.out | 7 ++++ 2 files changed, 103 insertions(+) create mode 100755 tests/xfs/554 create mode 100644 tests/xfs/554.out