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
7323S: Maintained7323S: Maintained
7324F: drivers/net/ethernet/nvidia/*7324F: drivers/net/ethernet/nvidia/*
7325 7325
7326FORTIFY_SOURCE
7327M: Kees Cook <keescook@chromium.org>
7328L: linux-hardening@vger.kernel.org
7329S: Supported
7330F: include/linux/fortify-string.h
7331F: lib/test_fortify/*
7332F: scripts/test_fortify.sh
7333K: \b__NO_FORTIFY\b
7334
7326FPGA DFL DRIVERS7335FPGA DFL DRIVERS
7327M: Wu Hao <hao.wu@intel.com>7336M: Wu Hao <hao.wu@intel.com>
7328R: Tom Rix <trix@redhat.com>7337R: Tom Rix <trix@redhat.com>
lib/.gitignore+2−0
@@ -4,3 +4,5 @@ · 3 unmodified lines
4/gen_crc32table4/gen_crc32table
5/gen_crc64table5/gen_crc64table
6/oid_registry_data.c6/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
360obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o360obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o
361 361
362obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o362obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
363
364# FORTIFY_SOURCE compile-time behavior tests
365TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
366TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
367TEST_FORTIFY_LOG = test_fortify.log
368
369quiet_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
374targets += $(TEST_FORTIFY_LOGS)
375clean-files += $(TEST_FORTIFY_LOGS)
376clean-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
384quiet_cmd_gen_fortify_log = GEN $@
385 cmd_gen_fortify_log = cat </dev/null $(filter-out FORCE,$^) 2>/dev/null > $@ || true
386
387targets += $(TEST_FORTIFY_LOG)
388clean-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.
393ifeq ($(CONFIG_FORTIFY_SOURCE),y)
394$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
395endif
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
7void do_fortify_tests(void);
8
9#define __BUF_SMALL 16
10#define __BUF_LARGE 32
11struct 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"
19const char small_src[__BUF_SMALL] = LITERAL_SMALL;
20const char large_src[__BUF_LARGE] = LITERAL_LARGE;
21
22char small[__BUF_SMALL];
23char large[__BUF_LARGE];
24struct fortify_object instance;
25size_t size;
26
27void 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
3set -e
4
5# Argument 1: Source file to build.
6IN="$1"
7shift
8# Extract just the filename for error messages below.
9FILE="${IN##*/}"
10# Extract the function name for error messages below.
11FUNC="${FILE#*-}"
12FUNC="${FUNC%%-*}"
13FUNC="${FUNC%%.*}"
14# Extract the symbol to test for in build/symbol test below.
15WANT="__${FILE%%-*}"
16
17# Argument 2: Where to write the build log.
18OUT="$1"
19shift
20TMP="${OUT}.tmp"
21
22# Argument 3: Path to "nm" tool.
23NM="$1"
24shift
25
26# Remaining arguments are: $(CC) $(c_flags)
27
28# Clean up temporary file at exit.
29__cleanup() {
30 rm -f "$TMP"
31}
32trap __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.
36export LANG=C
37
38status=
39# Attempt to build a source that is expected to fail with a specific warning.
40if "$@" -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
48else
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
53fi
54
55if [ -n "$status" ]; then
56 # Report on failure results, including compilation warnings.
57 echo "$status" | tee "$OUT" >&2
58else
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"
61fi
62cat "$TMP" >>"$OUT"