Files
apfstests/common/fuzzy
T
Darrick J. Wong 0804dc1736 xfs: fuzz every field of every structure and test kernel crashes
Fuzz every field of every structure and then try to write the
filesystem, to see how many of these writes can crash the kernel.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
2018-07-08 21:16:35 +08:00

315 lines
8.5 KiB
Plaintext

##/bin/bash
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2017 Oracle. All Rights Reserved.
#
# Routines for fuzzing and scrubbing a filesystem.
# Modify various files after a fuzzing operation
_scratch_fuzz_modify() {
nr="$1"
test -z "${nr}" && nr=50000
echo "+++ touch ${nr} files"
blk_sz=$(stat -f -c '%s' ${SCRATCH_MNT})
$XFS_IO_PROG -f -c "pwrite -S 0x63 0 ${blk_sz}" "/tmp/afile" > /dev/null
date="$(date)"
find "${SCRATCH_MNT}/" -type f 2> /dev/null | head -n "${nr}" | while read f; do
setfattr -n "user.date" -v "${date}" "$f"
cat "/tmp/afile" >> "$f"
mv "$f" "$f.longer"
done
sync
rm -rf "/tmp/afile"
echo "+++ create files"
mkdir -p "${SCRATCH_MNT}/test.moo"
$XFS_IO_PROG -f -c 'pwrite -S 0x80 0 65536' "${SCRATCH_MNT}/test.moo/urk" > /dev/null
sync
echo "+++ remove files"
rm -rf "${SCRATCH_MNT}/test.moo"
}
# Try to access files after fuzzing
_scratch_fuzz_test() {
echo "+++ ls -laR" >> $seqres.full
ls -laR "${SCRATCH_MNT}/test.1/" >/dev/null 2>&1
echo "+++ cat files" >> $seqres.full
(find "${SCRATCH_MNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat) >/dev/null 2>&1
}
# Do we have an online scrub program?
_require_scrub() {
case "${FSTYP}" in
"xfs")
test -x "$XFS_SCRUB_PROG" || _notrun "xfs_scrub not found"
;;
*)
_notrun "No online scrub program for ${FSTYP}."
;;
esac
}
# Scrub the scratch filesystem metadata (online)
_scratch_scrub() {
case "${FSTYP}" in
"xfs")
$XFS_SCRUB_PROG -d -T -v "$@" $SCRATCH_MNT
;;
*)
_fail "No online scrub program for ${FSTYP}."
;;
esac
}
# Filter out any keys with an array index >= 10, collapse any array range
# ("[1-195]") to the first item, and ignore padding fields.
__filter_xfs_db_keys() {
sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' \
-e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g' \
-e '/pad/d'
}
# Filter the xfs_db print command's field debug information
# into field name and type.
__filter_xfs_db_print_fields() {
filter="$1"
if [ -z "${filter}" ] || [ "${filter}" = "nofilter" ]; then
filter='^'
fi
grep ' = ' | while read key equals value; do
fuzzkey="$(echo "${key}" | __filter_xfs_db_keys)"
if [ -z "${fuzzkey}" ]; then
continue
elif [[ "${value}" == "["* ]]; then
echo "${value}" | sed -e 's/^.//g' -e 's/.$//g' -e 's/,/\n/g' | while read subfield; do
echo "${fuzzkey}.${subfield}"
done | __filter_xfs_db_keys
else
echo "${fuzzkey}"
fi
done | egrep "${filter}"
}
# Navigate to some part of the filesystem and print the field info.
# The first argument is an egrep filter for the fields
# The rest of the arguments are xfs_db commands to locate the metadata.
_scratch_xfs_list_metadata_fields() {
filter="$1"
shift
if [ -n "${SCRATCH_XFS_LIST_METADATA_FIELDS}" ]; then
echo "${SCRATCH_XFS_LIST_METADATA_FIELDS}" | tr '[ ,]' '[\n\n]'
return;
fi
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
_scratch_xfs_db "${cmds[@]}" -c print | __filter_xfs_db_print_fields "${filter}"
}
# Fuzz a metadata field
# The first arg is the field name
# The second arg is the xfs_db fuzz verb
# The rest of the arguments are xfs_db commands to find the metadata.
_scratch_xfs_fuzz_metadata_field() {
key="$1"
value="$2"
shift; shift
if [[ "${key}" == *crc ]]; then
fuzz_arg="-c"
else
fuzz_arg="-d"
fi
oldval="$(_scratch_xfs_get_metadata_field "${key}" "$@")"
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
while true; do
_scratch_xfs_db -x "${cmds[@]}" -c "fuzz ${fuzz_arg} ${key} ${value}"
echo
newval="$(_scratch_xfs_get_metadata_field "${key}" "$@" 2> /dev/null)"
if [ "${key}" != "random" ] || [ "${oldval}" != "${newval}" ]; then
break;
fi
done
if [ "${oldval}" = "${newval}" ]; then
echo "Field ${key} already set to ${newval}, skipping test."
return 1
fi
return 0
}
# Try to forcibly unmount the scratch fs
__scratch_xfs_fuzz_unmount()
{
while _scratch_unmount 2>/dev/null; do sleep 0.2; done
}
# Restore metadata to scratch device prior to field-fuzzing.
__scratch_xfs_fuzz_mdrestore()
{
test -e "${POPULATE_METADUMP}" || _fail "Need to set POPULATE_METADUMP"
__scratch_xfs_fuzz_unmount
xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}"
}
__fuzz_notify() {
echo "$@"
test -w /dev/ttyprintk && echo "$@" >> /dev/ttyprintk
}
# Fuzz one field of some piece of metadata.
# First arg is the field name
# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
# Third arg is the repair mode (online, offline, both, none)
__scratch_xfs_fuzz_field_test() {
field="$1"
fuzzverb="$2"
repair="$3"
shift; shift; shift
# Set the new field value
__fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
echo "========================"
_scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
res=$?
test $res -ne 0 && return
# Try to catch the error with scrub
echo "+ Try to catch the error"
_try_scratch_mount 2>&1
res=$?
if [ $res -eq 0 ]; then
# Try an online scrub unless we're fuzzing ag 0's sb,
# which scrub doesn't know how to fix.
if [ "${repair}" != "none" ]; then
echo "++ Online scrub"
if [ "$1" != "sb 0" ]; then
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "scrub didn't fail with ${field} = ${fuzzverb}.")
fi
fi
# Try fixing the filesystem online?!
if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
__fuzz_notify "++ Try to repair filesystem online"
_scratch_scrub 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "online repair failed ($res) with ${field} = ${fuzzverb}.")
fi
__scratch_xfs_fuzz_unmount
elif [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
(>&2 echo "mount failed ($res) with ${field} = ${fuzzverb}.")
fi
# Repair the filesystem offline?
if [ "${repair}" = "offline" ] || [ "${repair}" = "both" ]; then
echo "+ Try to repair the filesystem offline"
_repair_scratch_fs 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "offline repair failed ($res) with ${field} = ${fuzzverb}.")
fi
# See if repair finds a clean fs
if [ "${repair}" != "none" ]; then
echo "+ Make sure error is gone (offline)"
_scratch_xfs_repair -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "offline re-scrub ($res) with ${field} = ${fuzzverb}.")
fi
# See if scrub finds a clean fs
echo "+ Make sure error is gone (online)"
_try_scratch_mount 2>&1
res=$?
if [ $res -eq 0 ]; then
# Try an online scrub unless we're fuzzing ag 0's sb,
# which scrub doesn't know how to fix.
if [ "${repair}" != "none" ]; then
echo "++ Online scrub"
if [ "$1" != "sb 0" ]; then
_scratch_scrub -n -e continue 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "online re-scrub ($res) with ${field} = ${fuzzverb}.")
fi
fi
# Try modifying the filesystem again!
__fuzz_notify "++ Try to write filesystem again"
_scratch_fuzz_modify 100 2>&1
__scratch_xfs_fuzz_unmount
else
(>&2 echo "re-mount failed ($res) with ${field} = ${fuzzverb}.")
fi
# See if repair finds a clean fs
if [ "${repair}" != "none" ]; then
echo "+ Re-check the filesystem (offline)"
_scratch_xfs_repair -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "re-repair failed ($res) with ${field} = ${fuzzverb}.")
fi
}
# Make sure we have all the pieces we need for field fuzzing
_require_scratch_xfs_fuzz_fields()
{
_require_scratch_nocheck
_require_scrub
_require_populate_commands
_scratch_mkfs_xfs >/dev/null 2>&1
_require_xfs_db_command "fuzz"
}
# Grab the list of available fuzzing verbs
_scratch_xfs_list_fuzz_verbs() {
if [ -n "${SCRATCH_XFS_LIST_FUZZ_VERBS}" ]; then
echo "${SCRATCH_XFS_LIST_FUZZ_VERBS}" | tr '[ ,]' '[\n\n]'
return;
fi
_scratch_xfs_db -x -c 'sb 0' -c 'fuzz' | grep '^Fuzz commands:' | \
sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g'
}
# Fuzz some of the fields of some piece of metadata
# The first argument is an egrep filter for the field names
# The second argument is the repair mode (online, offline, both)
# The rest of the arguments are xfs_db commands to locate the metadata.
#
# Users can specify the fuzz verbs via SCRATCH_XFS_LIST_FUZZ_VERBS
# They can specify the fields via SCRATCH_XFS_LIST_METADATA_FIELDS
_scratch_xfs_fuzz_metadata() {
filter="$1"
repair="$2"
shift; shift
fields="$(_scratch_xfs_list_metadata_fields "${filter}" "$@")"
verbs="$(_scratch_xfs_list_fuzz_verbs)"
echo "Fields we propose to fuzz under: $@"
echo $(echo "${fields}")
echo "Verbs we propose to fuzz with:"
echo $(echo "${verbs}")
echo "${fields}" | while read field; do
echo "${verbs}" | while read fuzzverb; do
__scratch_xfs_fuzz_mdrestore
__scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"
done
done
}