History

fortify: Add compile-time FORTIFY_SOURCE tests

While the run-time testing of FORTIFY_SOURCE is already present in LKDTM, there is no testing of the expected compile-time detections. In preparation for correctly supporting FORTIFY_SOURCE under Clang, adding additional FORTIFY_SOURCE defenses, and making sure FORTIFY_SOURCE doesn't silently regress with GCC, introduce a build-time test suite that checks each expected compile-time failure condition. As this is relatively backwards from standard build rules in the sense that a successful test is actually a compile _failure_, create a wrapper script to check for the correct errors, and wire it up as a dummy dependency to lib/string.o, collecting the results into a log file artifact.

Kees Cook committed 5 years ago

Commit be58f711 parent 3009f8922 files · +226 −0Browse files

Unified · Split

22 files
  1. MAINTAINERS +9 −0
  2. lib/.gitignore +2 −0
  3. lib/Makefile +33 −0
  4. lib/test_fortify/read_overflow-memchr.c +5 −0
  5. lib/test_fortify/read_overflow-memchr_inv.c +5 −0
  6. lib/test_fortify/read_overflow-memcmp.c +5 −0
  7. lib/test_fortify/read_overflow-memscan.c +5 −0
  8. lib/test_fortify/read_overflow2-memcmp.c +5 −0
  9. lib/test_fortify/read_overflow2-memcpy.c +5 −0
  10. lib/test_fortify/read_overflow2-memmove.c +5 −0
  11. lib/test_fortify/test_fortify.h +35 −0
  12. lib/test_fortify/write_overflow-memcpy.c +5 −0
  13. lib/test_fortify/write_overflow-memmove.c +5 −0
  14. lib/test_fortify/write_overflow-memset.c +5 −0
  15. lib/test_fortify/write_overflow-strcpy-lit.c +5 −0
  16. lib/test_fortify/write_overflow-strcpy.c +5 −0
  17. lib/test_fortify/write_overflow-strlcpy-src.c +5 −0
  18. lib/test_fortify/write_overflow-strlcpy.c +5 −0
  19. lib/test_fortify/write_overflow-strncpy-src.c +5 −0
  20. lib/test_fortify/write_overflow-strncpy.c +5 −0
  21. lib/test_fortify/write_overflow-strscpy.c +5 −0
  22. scripts/test_fortify.sh +62 −0
MAINTAINERS+9−0
@@ -7323,6 +7323,15 @@ L: netdev@vger.kernel.org · 7322 unmodified lines
73237323 S: Maintained
73247324 F: drivers/net/ethernet/nvidia/*
73257325
7326+FORTIFY_SOURCE
7327+M: Kees Cook <keescook@chromium.org>
7328+L: linux-hardening@vger.kernel.org
7329+S: Supported
7330+F: include/linux/fortify-string.h
7331+F: lib/test_fortify/*
7332+F: scripts/test_fortify.sh
7333+K: \b__NO_FORTIFY\b
7334+
73267335 FPGA DFL DRIVERS
73277336 M: Wu Hao <hao.wu@intel.com>
73287337 R: Tom Rix <trix@redhat.com>
lib/.gitignore+2−0
@@ -4,3 +4,5 @@ · 3 unmodified lines
44 /gen_crc32table
55 /gen_crc64table
66 /oid_registry_data.c
7+/test_fortify.log
8+/test_fortify/*.log
lib/Makefile+33−0
@@ -360,3 +360,36 @@ obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o · 359 unmodified lines
360360 obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
361361
362362 obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
363+
364+# FORTIFY_SOURCE compile-time behavior tests
365+TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
366+TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
367+TEST_FORTIFY_LOG = test_fortify.log
368+
369+quiet_cmd_test_fortify = TEST $@
370+ cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \
371+ $< $@ "$(NM)" $(CC) $(c_flags) \
372+ $(call cc-disable-warning,fortify-source)
373+
374+targets += $(TEST_FORTIFY_LOGS)
375+clean-files += $(TEST_FORTIFY_LOGS)
376+clean-files += $(addsuffix .o, $(TEST_FORTIFY_LOGS))
377+$(obj)/test_fortify/%.log: $(src)/test_fortify/%.c \
378+ $(src)/test_fortify/test_fortify.h \
379+ $(srctree)/include/linux/fortify-string.h \
380+ $(srctree)/scripts/test_fortify.sh \
381+ FORCE
382+ $(call if_changed,test_fortify)
383+
384+quiet_cmd_gen_fortify_log = GEN $@
385+ cmd_gen_fortify_log = cat </dev/null $(filter-out FORCE,$^) 2>/dev/null > $@ || true
386+
387+targets += $(TEST_FORTIFY_LOG)
388+clean-files += $(TEST_FORTIFY_LOG)
389+$(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
390+ $(call if_changed,gen_fortify_log)
391+
392+# Fake dependency to trigger the fortify tests.
393+ifeq ($(CONFIG_FORTIFY_SOURCE),y)
394+$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
395+endif
lib/test_fortify/read_overflow-memchr.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memchr(small, 0x7A, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow-memchr_inv.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memchr_inv(small, 0x7A, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow-memcmp.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memcmp(small, large, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow-memscan.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memscan(small, 0x7A, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow2-memcmp.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memcmp(large, small, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow2-memcpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memcpy(large, instance.buf, sizeof(large))
4+
5+#include "test_fortify.h"
lib/test_fortify/read_overflow2-memmove.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memmove(large, instance.buf, sizeof(large))
4+
5+#include "test_fortify.h"
lib/test_fortify/test_fortify.h+35−0
@@ -0,0 +1,35 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+#include <linux/kernel.h>
3+#include <linux/printk.h>
4+#include <linux/slab.h>
5+#include <linux/string.h>
6+
7+void do_fortify_tests(void);
8+
9+#define __BUF_SMALL 16
10+#define __BUF_LARGE 32
11+struct fortify_object {
12+ int a;
13+ char buf[__BUF_SMALL];
14+ int c;
15+};
16+
17+#define LITERAL_SMALL "AAAAAAAAAAAAAAA"
18+#define LITERAL_LARGE "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
19+const char small_src[__BUF_SMALL] = LITERAL_SMALL;
20+const char large_src[__BUF_LARGE] = LITERAL_LARGE;
21+
22+char small[__BUF_SMALL];
23+char large[__BUF_LARGE];
24+struct fortify_object instance;
25+size_t size;
26+
27+void do_fortify_tests(void)
28+{
29+ /* Normal initializations. */
30+ memset(&instance, 0x32, sizeof(instance));
31+ memset(small, 0xA5, sizeof(small));
32+ memset(large, 0x5A, sizeof(large));
33+
34+ TEST;
35+}
lib/test_fortify/write_overflow-memcpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memcpy(instance.buf, large_src, sizeof(large_src))
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-memmove.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memmove(instance.buf, large_src, sizeof(large_src))
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-memset.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ memset(instance.buf, 0x5A, sizeof(large_src))
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strcpy-lit.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strcpy(small, LITERAL_LARGE)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strcpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strcpy(small, large_src)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strlcpy-src.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strlcpy(small, large_src, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strlcpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strlcpy(instance.buf, large_src, sizeof(instance.buf) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strncpy-src.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strncpy(small, large_src, sizeof(small) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strncpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strncpy(instance.buf, large_src, sizeof(instance.buf) + 1)
4+
5+#include "test_fortify.h"
lib/test_fortify/write_overflow-strscpy.c+5−0
@@ -0,0 +1,5 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#define TEST \
3+ strscpy(instance.buf, large_src, sizeof(instance.buf) + 1)
4+
5+#include "test_fortify.h"
scripts/test_fortify.sh+62−0
@@ -0,0 +1,62 @@
1+#!/bin/sh
2+# SPDX-License-Identifier: GPL-2.0-only
3+set -e
4+
5+# Argument 1: Source file to build.
6+IN="$1"
7+shift
8+# Extract just the filename for error messages below.
9+FILE="${IN##*/}"
10+# Extract the function name for error messages below.
11+FUNC="${FILE#*-}"
12+FUNC="${FUNC%%-*}"
13+FUNC="${FUNC%%.*}"
14+# Extract the symbol to test for in build/symbol test below.
15+WANT="__${FILE%%-*}"
16+
17+# Argument 2: Where to write the build log.
18+OUT="$1"
19+shift
20+TMP="${OUT}.tmp"
21+
22+# Argument 3: Path to "nm" tool.
23+NM="$1"
24+shift
25+
26+# Remaining arguments are: $(CC) $(c_flags)
27+
28+# Clean up temporary file at exit.
29+__cleanup() {
30+ rm -f "$TMP"
31+}
32+trap __cleanup EXIT
33+
34+# Function names in warnings are wrapped in backticks under UTF-8 locales.
35+# Run the commands with LANG=C so that grep output will not change.
36+export LANG=C
37+
38+status=
39+# Attempt to build a source that is expected to fail with a specific warning.
40+if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
41+ # If the build succeeds, either the test has failed or the
42+ # warning may only happen at link time (Clang). In that case,
43+ # make sure the expected symbol is unresolved in the symbol list.
44+ # If so, FORTIFY is working for this case.
45+ if ! $NM -A "$OUT".o | grep -m1 "\bU ${WANT}$" >>"$TMP" ; then
46+ status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
47+ fi
48+else
49+ # If the build failed, check for the warning in the stderr (gcc).
50+ if ! grep -q -m1 "error: call to .\b${WANT}\b." "$TMP" ; then
51+ status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN"
52+ fi
53+fi
54+
55+if [ -n "$status" ]; then
56+ # Report on failure results, including compilation warnings.
57+ echo "$status" | tee "$OUT" >&2
58+else
59+ # Report on good results, and save any compilation output to log.
60+ echo "ok: unsafe ${FUNC}() usage correctly detected with '$WANT' in $IN" >"$OUT"
61+fi
62+cat "$TMP" >>"$OUT"