[RFC PATCH xfstests] xfs: add tests to validate ioctl structure layout.

Nick Bowler
2018-12-14T03:36:51Z

Introduce two new test cases. The first one greps xfs_fs.h to find
any structure types possibly relevant to ioctls, then compares the
structure layouts between 64-bit and 32-bit objects. The goal is to
ensure that any _new_ structure has the same characteristics on 32-bit
versus 64-bit mode, ensuring a sane compat handler. This requires a
toolchain that can build both 64-bit and 32-bit objects, and hopefully
works on non-x86 platforms with compat woes.

The whitelist was constructed by manually inspecting the current
compat ioctl implementation. The test validates that everything
in the whitelist actually got found by the grepping as a sort of
self-sanity check, but if older versions lacked some of these
types then this might need tweaking a bit.

The second test validates that a well-known set of structures on x32
crrectly match the ia32 or amd64 layouts as expected. This requires
a toolchain that can build for all three ABIs.

The idea is that new additions to the header where the structures
differ in 32 and 64-bit x86 will be caught by the first test case
and that will be sufficient to detect potential x32 problems too.

Signed-off-by: Nick Bowler <nbowler@draconx.ca>
---
.gitignore | 1 +
common/config | 1 +
configure.ac | 2 +-
include/build-env.in | 6 +++
tests/xfs/499 | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/499.out | 16 ++++++++
tests/xfs/500 | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/500.out | 2 +
tests/xfs/group | 2 +
9 files changed, 236 insertions(+), 1 deletion(-)
create mode 100644 include/build-env.in
create mode 100755 tests/xfs/499
create mode 100644 tests/xfs/499.out
create mode 100755 tests/xfs/500
create mode 100644 tests/xfs/500.out

diff --git a/.gitignore b/.gitignore
index ea1aac8a..7bfd20fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@
/ltmain.sh

# build system
+/include/build-env
/include/builddefs
/include/config.h
/include/config.h.in
diff --git a/common/config b/common/config
index a87cb4a2..707c5b2f 100644
--- a/common/config
+++ b/common/config
@@ -194,6 +194,7 @@ export GETCAP_PROG="$(type -P getcap)"
export CHECKBASHISMS_PROG="$(type -P checkbashisms)"
export XFS_INFO_PROG="$(type -P xfs_info)"
export DUPEREMOVE_PROG="$(type -P duperemove)"
+export PAHOLE_PROG="$(type -P pahole)"

# use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
# newer systems have udevadm command but older systems like RHEL5 don't.
diff --git a/configure.ac b/configure.ac
index 19798824..69a4e396 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,5 +73,5 @@ AC_HAVE_COPY_FILE_RANGE
AC_CHECK_FUNCS([renameat2])

AC_CONFIG_HEADER(include/config.h)
-AC_CONFIG_FILES([include/builddefs])
+AC_CONFIG_FILES([include/builddefs include/build-env])
AC_OUTPUT
diff --git a/include/build-env.in b/include/build-env.in
new file mode 100644
index 00000000..9a9a6270
--- /dev/null
+++ b/include/build-env.in
@@ -0,0 +1,6 @@
+# Generated by config.status to export build toolchain information
+# for use in testcases.
+
+: "${CC_PROG=`command -v @CC@`}"
+: "${CPPFLAGS=@CPPFLAGS@}"
+: "${CFLAGS=@CFLAGS@}"
diff --git a/tests/xfs/499 b/tests/xfs/499
new file mode 100755
index 00000000..48df2711
--- /dev/null
+++ b/tests/xfs/499
@@ -0,0 +1,102 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018 Nick Bowler. All Rights Reserved.
+#
+# Scan header files for userspace-visible structures and look for newly
+# introduced compat problems between 32 and 64 bit flavours. Anything
+# not on a whitelist is expected to have identical representation on
+# both versions.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+tmp=/tmp/$$
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./include/build-env
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+_require_command "$CC_PROG" cc
+_require_command "$PAHOLE_PROG" pahole
+
+compile()
+{
+ $CC_PROG $CPPFLAGS $CFLAGS "$@"
+}
+
+# Toolchain check!
+compile -m64 -include xfs/xfs.h -gdwarf-2 -c -xc /dev/null -o /dev/null ||
+ _notrun 'cannot build 64-bit objects'
+compile -m32 -D_FILE_OFFSET_BITS=64 -include xfs/xfs.h -gdwarf-2 -c \
+ -xc /dev/null -o /dev/null || _notrun 'cannot build 32-bit objects'
+
+# Collect XFS types from "xfs/xfs_fs.h". We look for struct tags and
+# identifiers that look like xfs_xyz_t under the assumption that all
+# ioctl-relevant structures are at least _named_ in this file.
+compile -E -include xfs/xfs_fs.h -xc /dev/null >"$tmp.hdump" ||
+ _notrun 'cannot #include "xfs/xfs_fs.h"'
+
+sed -nf- "$tmp.hdump" <<'EOF' | LC_ALL=C sort -u >"$tmp.types"
+ h
+ s/.*[^_[:alpha:]]\(xfs_[_[:alnum:]]*_t\).*/\1/p
+ g
+ s/.*struct \([_[:alpha:]][_[:alnum:]]*\).*/struct \1/p
+EOF
+
+# Create a source file containing definitions of objects for every type we
+# found, enabling pahole to report on those types.
+{ printf '#include "xfs/xfs.h"\n'
+
+ n=0
+ exec 3<"$tmp.types" 4>"$tmp.mustmatch"
+ while read t <&3; do
+ case $t in
+ # Whitelist structures known to potentially differ between
+ # 32-bit and 64-bit compilations. NOTE: if new structures
+ # get added to this list it is probably necessary to update
+ # xfs/500 test case as well to set the expected x32 behaviour.
+ "struct xfs_attr_multiop" | \
+ "struct xfs_bstat" | \
+ "struct xfs_bstime" | \
+ "struct xfs_flock64" | \
+ "struct xfs_fsop_attrlist_handlereq" | \
+ "struct xfs_fsop_attrmulti_handlereq" | \
+ "struct xfs_fsop_bulkreq" | \
+ "struct xfs_fsop_geom_v1" | \
+ "struct xfs_fsop_handlereq" | \
+ "struct xfs_fsop_setdm_handlereq" | \
+ "struct xfs_growfs_data" | \
+ "struct xfs_growfs_rt" | \
+ "struct xfs_inogrp" | \
+ "struct xfs_swapext")
+ printf 'whitelist %s\n' "$t" 1>&2 ;;
+ *) printf '%s\n' "${t#struct }" >&4 ;; # everything else must match!
+ esac
+ printf '%s var%d = {0};\n' "$t" "$n"
+ n=$((n+1))
+ done
+ exec 3<&- 4>&-
+ } >"$tmp.test.c"
+
+compile -m32 -D_FILE_OFFSET_BITS=64 -gdwarf-2 -c "$tmp.test.c" -o "$tmp.32.o"
+$PAHOLE_PROG --class_name="file://$tmp.mustmatch" "$tmp.32.o" >"$tmp.32.dat"
+
+compile -m64 -gdwarf-2 -c "$tmp.test.c" -o "$tmp.64.o"
+$PAHOLE_PROG --class_name="file://$tmp.mustmatch" "$tmp.64.o" >"$tmp.64.dat"
+
+printf 'Silence is golden\n'
+diff -u "$tmp.32.dat" "$tmp.64.dat"
+status=$?
diff --git a/tests/xfs/499.out b/tests/xfs/499.out
new file mode 100644
index 00000000..80e26046
--- /dev/null
+++ b/tests/xfs/499.out
@@ -0,0 +1,16 @@
+QA output created by 499
+whitelist struct xfs_attr_multiop
+whitelist struct xfs_bstat
+whitelist struct xfs_bstime
+whitelist struct xfs_flock64
+whitelist struct xfs_fsop_attrlist_handlereq
+whitelist struct xfs_fsop_attrmulti_handlereq
+whitelist struct xfs_fsop_bulkreq
+whitelist struct xfs_fsop_geom_v1
+whitelist struct xfs_fsop_handlereq
+whitelist struct xfs_fsop_setdm_handlereq
+whitelist struct xfs_growfs_data
+whitelist struct xfs_growfs_rt
+whitelist struct xfs_inogrp
+whitelist struct xfs_swapext
+Silence is golden
diff --git a/tests/xfs/500 b/tests/xfs/500
new file mode 100755
index 00000000..80d37b0f
--- /dev/null
+++ b/tests/xfs/500
@@ -0,0 +1,105 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018 Nick Bowler. All Rights Reserved.
+#
+# FS QA Test 500
+#
+# Check that x32 ioctl-related structures are aligned with the ia32 or
+# amd64 variants as expected.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+tmp=/tmp/$$
+status=1 # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./include/build-env
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+_require_command "$CC_PROG" cc
+_require_command "$PAHOLE_PROG" pahole
+
+compile()
+{
+ $CC_PROG $CPPFLAGS $CFLAGS "$@"
+}
+
+# Toolchain check!
+compile -mx32 -include xfs/xfs.h -gdwarf-2 -c -xc /dev/null -o /dev/null ||
+ _notrun 'cannot build x32 objects'
+compile -m64 -include xfs/xfs.h -gdwarf-2 -c -xc /dev/null -o /dev/null ||
+ _notrun 'cannot build amd64 objects'
+compile -m32 -D_FILE_OFFSET_BITS=64 -include xfs/xfs.h -gdwarf-2 -c \
+ -xc /dev/null -o /dev/null || _notrun 'cannot build ia32 objects'
+
+# struct tags where x32 is expected to match ia32 layout ...
+cat >"$tmp.ia32structs" <<'EOF'
+fsdmidata
+xfs_attr_multiop
+xfs_attrlist_cursor
+xfs_fsop_attrlist_handlereq
+xfs_fsop_attrmulti_handlereq
+xfs_fsop_bulkreq
+xfs_fsop_handlereq
+xfs_fsop_setdm_handlereq
+EOF
+
+# ... and the tags where x32 is expected to match amd64.
+cat >"$tmp.amd64structs" <<'EOF'
+fsdmidata
+xfs_attrlist_cursor
+xfs_bstat
+xfs_bstime
+xfs_flock64
+xfs_fsop_geom_v1
+xfs_growfs_data
+xfs_growfs_rt
+xfs_inogrp
+xfs_swapext
+EOF
+
+# Create source file containing definitions of objects with each type we care
+# about, in order to produce relevant debug information which will be checked
+# by pahole.
+{ printf '#include "xfs/xfs.h"\n'
+
+ n=0
+ cat "$tmp.ia32structs" "$tmp.amd64structs" | while read l; do
+ printf 'struct %s var%d = {0};\n' "$l" "$n"
+ n=$((n+1))
+ done
+ } >"$tmp.test.c"
+
+compile -m32 -D_FILE_OFFSET_BITS=64 -gdwarf-2 -c "$tmp.test.c" -o "$tmp.ia32.o"
+compile -mx32 -gdwarf-2 -c "$tmp.test.c" -o "$tmp.x32.o"
+compile -m64 -gdwarf-2 -c "$tmp.test.c" -o "$tmp.amd64.o"
+
+$PAHOLE_PROG --class_name="file://$tmp.ia32structs" "$tmp.ia32.o" \
+ >"$tmp.ia32.dat"
+$PAHOLE_PROG --class_name="file://$tmp.ia32structs" "$tmp.x32.o" \
+ >"$tmp.x32-ia32.dat"
+
+$PAHOLE_PROG --class_name="file://$tmp.amd64structs" "$tmp.amd64.o" \
+ >"$tmp.amd64.dat"
+$PAHOLE_PROG --class_name="file://$tmp.amd64structs" "$tmp.x32.o" \
+ >"$tmp.x32-amd64.dat"
+
+printf 'Silence is golden\n'
+
+ret=0
+diff -u "$tmp.ia32.dat" "$tmp.x32-ia32.dat" || ret=1
+diff -u "$tmp.amd64.dat" "$tmp.x32-amd64.dat" || ret=1
+status=$ret
diff --git a/tests/xfs/500.out b/tests/xfs/500.out
new file mode 100644
index 00000000..883b2caf
--- /dev/null
+++ b/tests/xfs/500.out
@@ -0,0 +1,2 @@
+QA output created by 500
+Silence is golden
diff --git a/tests/xfs/group b/tests/xfs/group
index dfaae2bc..cb69d28a 100644
--- a/tests/xfs/group
+++ b/tests/xfs/group
@@ -496,3 +496,5 @@
496 dangerous_fuzzers dangerous_scrub dangerous_repair
497 dangerous_fuzzers dangerous_scrub dangerous_online_repair
498 dangerous_fuzzers dangerous_norepair
+499 auto quick
+500 auto quick
--
2.16.1