History

Merge tag 'hardening-v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull hardening updates from Kees Cook: "As is pretty normal for this tree, there are changes all over the place, especially for small fixes, selftest improvements, and improved macro usability. Some header changes ended up landing via this tree as they depended on the string header cleanups. Also, a notable set of changes is the work for the reintroduction of the UBSAN signed integer overflow sanitizer so that we can continue to make improvements on the compiler side to make this sanitizer a more viable future security hardening option. Summary: - string.h and related header cleanups (Tanzir Hasan, Andy Shevchenko) - VMCI memcpy() usage and struct_size() cleanups (Vasiliy Kovalev, Harshit Mogalapalli) - selftests/powerpc: Fix load_unaligned_zeropad build failure (Michael Ellerman) - hardened Kconfig fragment updates (Marco Elver, Lukas Bulwahn) - Handle tail call optimization better in LKDTM (Douglas Anderson) - Use long form types in overflow.h (Andy Shevchenko) - Add flags param to string_get_size() (Andy Shevchenko) - Add Coccinelle script for potential struct_size() use (Jacob Keller) - Fix objtool corner case under KCFI (Josh Poimboeuf) - Drop 13 year old backward compat CAP_SYS_ADMIN check (Jingzi Meng) - Add str_plural() helper (Michal Wajdeczko, Kees Cook) - Ignore relocations in .notes section - Add comments to explain how __is_constexpr() works - Fix m68k stack alignment expectations in stackinit Kunit test - Convert string selftests to KUnit - Add KUnit tests for fortified string functions - Improve reporting during fortified string warnings - Allow non-type arg to type_max() and type_min() - Allow strscpy() to be called with only 2 arguments - Add binary mode to leaking_addresses scanner - Various small cleanups to leaking_addresses scanner - Adding wrapping_*() arithmetic helper - Annotate initial signed integer wrap-around in refcount_t - Add explicit UBSAN section to MAINTAINERS - Fix UBSAN self-test warnings - Simplify UBSAN build via removal of CONFIG_UBSAN_SANITIZE_ALL - Reintroduce UBSAN's signed overflow sanitizer" * tag 'hardening-v6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (51 commits) selftests/powerpc: Fix load_unaligned_zeropad build failure string: Convert helpers selftest to KUnit string: Convert selftest to KUnit sh: Fix build with CONFIG_UBSAN=y compiler.h: Explain how __is_constexpr() works overflow: Allow non-type arg to type_max() and type_min() VMCI: Fix possible memcpy() run-time warning in vmci_datagram_invoke_guest_handler() lib/string_helpers: Add flags param to string_get_size() x86, relocs: Ignore relocations in .notes section objtool: Fix UNWIND_HINT_{SAVE,RESTORE} across basic blocks overflow: Use POD in check_shl_overflow() lib: stackinit: Adjust target string to 8 bytes for m68k sparc: vdso: Disable UBSAN instrumentation kernel.h: Move lib/cmdline.c prototypes to string.h leaking_addresses: Provide mechanism to scan binary files leaking_addresses: Ignore input device status lines leaking_addresses: Use File::Temp for /tmp files MAINTAINERS: Update LEAKING_ADDRESSES details fortify: Improve buffer overflow reporting fortify: Add KUnit tests for runtime overflows ...

Linus Torvalds committed 2 years ago

Commit 216532e2 parents b32273e 3fe1eb471 files · +1949 −688Browse files

Showing the changes to scripts (5 of 71 files). Show all

Unified · Split

5 files
  1. scripts/Makefile.lib +4 −1
  2. scripts/Makefile.ubsan +4 −1
  3. scripts/coccinelle/api/string_choices.cocci +41 −0
  4. scripts/coccinelle/misc/struct_size.cocci +74 −0
  5. scripts/leaking_addresses.pl +74 −16
scripts/Makefile.lib+4−1
@@ -175,8 +175,11 @@ endif · 174 unmodified lines
175175
176176 ifeq ($(CONFIG_UBSAN),y)
177177 _c_flags += $(if $(patsubst n%,, \
178− $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \
178+ $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)y), \
179179 $(CFLAGS_UBSAN))
180+_c_flags += $(if $(patsubst n%,, \
181+ $(UBSAN_SIGNED_WRAP_$(basetarget).o)$(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SIGNED_WRAP)$(UBSAN_SANITIZE)y), \
182+ $(CFLAGS_UBSAN_SIGNED_WRAP))
180183 endif
181184
182185 ifeq ($(CONFIG_KCOV),y)
scripts/Makefile.ubsan+4−1
@@ -10,6 +10,9 @@ ubsan-cflags-$(CONFIG_UBSAN_DIV_ZERO) += -fsanitize=integer-divide-by-zero · 9 unmodified lines
1010 ubsan-cflags-$(CONFIG_UBSAN_UNREACHABLE) += -fsanitize=unreachable
1111 ubsan-cflags-$(CONFIG_UBSAN_BOOL) += -fsanitize=bool
1212 ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum
13−ubsan-cflags-$(CONFIG_UBSAN_TRAP) += -fsanitize-undefined-trap-on-error
13+ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined,-fsanitize-undefined-trap-on-error)
1414
1515 export CFLAGS_UBSAN := $(ubsan-cflags-y)
16+
17+ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
18+export CFLAGS_UBSAN_SIGNED_WRAP := $(ubsan-signed-wrap-cflags-y)
scripts/coccinelle/api/string_choices.cocci+41−0
@@ -0,0 +1,41 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/// Find places to use string_choices.h's various helpers.
3+//
4+// Confidence: Medium
5+// Options: --no-includes --include-headers
6+virtual patch
7+virtual context
8+virtual report
9+
10+@str_plural depends on patch@
11+expression E;
12+@@
13+(
14+- ((E == 1) ? "" : "s")
15++ str_plural(E)
16+|
17+- ((E != 1) ? "s" : "")
18++ str_plural(E)
19+|
20+- ((E > 1) ? "s" : "")
21++ str_plural(E)
22+)
23+
24+@str_plural_r depends on !patch exists@
25+expression E;
26+position P;
27+@@
28+(
29+* ((E@P == 1) ? "" : "s")
30+|
31+* ((E@P != 1) ? "s" : "")
32+|
33+* ((E@P > 1) ? "s" : "")
34+)
35+
36+@script:python depends on report@
37+p << str_plural_r.P;
38+e << str_plural_r.E;
39+@@
40+
41+coccilib.report.print_report(p[0], "opportunity for str_plural(%s)" % e)
scripts/coccinelle/misc/struct_size.cocci+74−0
@@ -0,0 +1,74 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+///
3+/// Check for code that could use struct_size().
4+///
5+// Confidence: Medium
6+// Author: Jacob Keller <jacob.e.keller@intel.com>
7+// Copyright: (C) 2023 Intel Corporation
8+// Options: --no-includes --include-headers
9+
10+virtual patch
11+virtual context
12+virtual org
13+virtual report
14+
15+// the overflow Kunit tests have some code which intentionally does not use
16+// the macros, so we want to ignore this code when reporting potential
17+// issues.
18+@overflow_tests@
19+identifier f = overflow_size_helpers_test;
20+@@
21+
22+f
23+
24+//----------------------------------------------------------
25+// For context mode
26+//----------------------------------------------------------
27+
28+@depends on !overflow_tests && context@
29+expression E1, E2;
30+identifier m;
31+@@
32+(
33+* (sizeof(*E1) + (E2 * sizeof(*E1->m)))
34+)
35+
36+//----------------------------------------------------------
37+// For patch mode
38+//----------------------------------------------------------
39+
40+@depends on !overflow_tests && patch@
41+expression E1, E2;
42+identifier m;
43+@@
44+(
45+- (sizeof(*E1) + (E2 * sizeof(*E1->m)))
46++ struct_size(E1, m, E2)
47+)
48+
49+//----------------------------------------------------------
50+// For org and report mode
51+//----------------------------------------------------------
52+
53+@r depends on !overflow_tests && (org || report)@
54+expression E1, E2;
55+identifier m;
56+position p;
57+@@
58+(
59+ (sizeof(*E1)@p + (E2 * sizeof(*E1->m)))
60+)
61+
62+@script:python depends on org@
63+p << r.p;
64+@@
65+
66+coccilib.org.print_todo(p[0], "WARNING should use struct_size")
67+
68+@script:python depends on report@
69+p << r.p;
70+@@
71+
72+msg="WARNING: Use struct_size"
73+coccilib.report.print_report(p[0], msg)
74+
scripts/leaking_addresses.pl+74−16
@@ -23,6 +23,7 @@ use strict; · 22 unmodified lines
2323 use POSIX;
2424 use File::Basename;
2525 use File::Spec;
26+use File::Temp qw/tempfile/;
2627 use Cwd 'abs_path';
2728 use Term::ANSIColor qw(:constants);
2829 use Getopt::Long qw(:config no_auto_abbrev);
@@ -51,10 +52,13 @@ my $input_raw = ""; # Read raw results from file instead of scanning. · 22 unmodified lines
5152 my $suppress_dmesg = 0; # Don't show dmesg in output.
5253 my $squash_by_path = 0; # Summary report grouped by absolute path.
5354 my $squash_by_filename = 0; # Summary report grouped by filename.
55+my $kallsyms_file = ""; # Kernel symbols file.
5456 my $kernel_config_file = ""; # Kernel configuration file.
5557 my $opt_32bit = 0; # Scan 32-bit kernel.
5658 my $page_offset_32bit = 0; # Page offset for 32-bit kernel.
5759
60+my @kallsyms = ();
61+
5862 # Skip these absolute paths.
5963 my @skip_abs = (
6064 '/proc/kmsg',
@@ -95,6 +99,8 @@ Options: · 34 unmodified lines
9599 --squash-by-path Show one result per unique path.
96100 --squash-by-filename Show one result per unique filename.
97101 --kernel-config-file=<file> Kernel configuration file (e.g /boot/config)
102+ --kallsyms=<file> Read kernel symbol addresses from file (for
103+ scanning binary files).
98104 --32-bit Scan 32-bit kernel.
99105 --page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234).
100106 -d, --debug Display debugging output.
@@ -115,6 +121,7 @@ GetOptions( · 14 unmodified lines
115121 'squash-by-path' => \$squash_by_path,
116122 'squash-by-filename' => \$squash_by_filename,
117123 'raw' => \$raw,
124+ 'kallsyms=s' => \$kallsyms_file,
118125 'kernel-config-file=s' => \$kernel_config_file,
119126 '32-bit' => \$opt_32bit,
120127 'page-offset-32-bit=o' => \$page_offset_32bit,
@@ -155,6 +162,25 @@ if ($output_raw) { · 34 unmodified lines
155162 select $fh;
156163 }
157164
165+if ($kallsyms_file) {
166+ open my $fh, '<', $kallsyms_file or die "$0: $kallsyms_file: $!\n";
167+ while (<$fh>) {
168+ chomp;
169+ my @entry = split / /, $_;
170+ my $addr_text = $entry[0];
171+ if ($addr_text !~ /^0/) {
172+ # TODO: Why is hex() so impossibly slow?
173+ my $addr = hex($addr_text);
174+ my $symbol = $entry[2];
175+ # Only keep kernel text addresses.
176+ my $long = pack("J", $addr);
177+ my $entry = [$long, $symbol];
178+ push @kallsyms, $entry;
179+ }
180+ }
181+ close $fh;
182+}
183+
158184 parse_dmesg();
159185 walk(@DIRS);
160186
@@ -221,6 +247,7 @@ sub get_kernel_config_option · 60 unmodified lines
221247 {
222248 my ($option) = @_;
223249 my $value = "";
250+ my $tmp_fh;
224251 my $tmp_file = "";
225252 my @config_files;
226253
@@ -228,7 +255,8 @@ sub get_kernel_config_option · 1 unmodified line
228255 if ($kernel_config_file ne "") {
229256 @config_files = ($kernel_config_file);
230257 } elsif (-R "/proc/config.gz") {
231− my $tmp_file = "/tmp/tmpkconf";
258+ ($tmp_fh, $tmp_file) = tempfile("config.gz-XXXXXX",
259+ UNLINK => 1);
232260
233261 if (system("gunzip < /proc/config.gz > $tmp_file")) {
234262 dprint("system(gunzip < /proc/config.gz) failed\n");
@@ -250,10 +278,6 @@ sub get_kernel_config_option · 15 unmodified lines
250278 }
251279 }
252280
253− if ($tmp_file ne "") {
254− system("rm -f $tmp_file");
255− }
256−
257281 return $value;
258282 }
259283
@@ -285,9 +309,10 @@ sub is_false_positive · 25 unmodified lines
285309 return is_false_positive_32bit($match);
286310 }
287311
288− # 64 bit false positives.
289−
290− if ($match =~ '\b(0x)?(f|F){16}\b' or
312+ # Ignore 64 bit false positives:
313+ # 0xfffffffffffffff[0-f]
314+ # 0x0000000000000000
315+ if ($match =~ '\b(0x)?(f|F){15}[0-9a-f]\b' or
291316 $match =~ '\b(0x)?0{16}\b') {
292317 return 1;
293318 }
@@ -304,7 +329,7 @@ sub is_false_positive_32bit · 10 unmodified lines
304329 my ($match) = @_;
305330 state $page_offset = get_page_offset();
306331
307− if ($match =~ '\b(0x)?(f|F){8}\b') {
332+ if ($match =~ '\b(0x)?(f|F){7}[0-9a-f]\b') {
308333 return 1;
309334 }
310335
@@ -347,18 +372,23 @@ sub is_in_vsyscall_memory_region · 36 unmodified lines
347372 # True if argument potentially contains a kernel address.
348373 sub may_leak_address
349374 {
350− my ($line) = @_;
375+ my ($path, $line) = @_;
351376 my $address_re;
352377
353− # Signal masks.
378+ # Ignore Signal masks.
354379 if ($line =~ '^SigBlk:' or
355380 $line =~ '^SigIgn:' or
356381 $line =~ '^SigCgt:') {
357382 return 0;
358383 }
359384
360− if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
361− $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {
385+ # Ignore input device reporting.
386+ # /proc/bus/input/devices: B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
387+ # /sys/devices/platform/i8042/serio0/input/input1/uevent: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
388+ # /sys/devices/platform/i8042/serio0/input/input1/capabilities/key: 402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
389+ if ($line =~ '\bKEY=[[:xdigit:]]{9,14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
390+ ($path =~ '\bkey$' and
391+ $line =~ '\b[[:xdigit:]]{9,14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b')) {
362392 return 0;
363393 }
364394
@@ -401,7 +431,7 @@ sub parse_dmesg · 36 unmodified lines
401431 {
402432 open my $cmd, '-|', 'dmesg';
403433 while (<$cmd>) {
404− if (may_leak_address($_)) {
434+ if (may_leak_address("dmesg", $_)) {
405435 print 'dmesg: ' . $_;
406436 }
407437 }
@@ -442,6 +472,25 @@ sub timed_parse_file · 34 unmodified lines
442472 }
443473 }
444474
475+sub parse_binary
476+{
477+ my ($file) = @_;
478+
479+ open my $fh, "<:raw", $file or return;
480+ local $/ = undef;
481+ my $bytes = <$fh>;
482+ close $fh;
483+
484+ foreach my $entry (@kallsyms) {
485+ my $addr = $entry->[0];
486+ my $symbol = $entry->[1];
487+ my $offset = index($bytes, $addr);
488+ if ($offset != -1) {
489+ printf("$file: $symbol @ $offset\n");
490+ }
491+ }
492+}
493+
445494 sub parse_file
446495 {
447496 my ($file) = @_;
@@ -451,13 +500,22 @@ sub parse_file · 3 unmodified lines
451500 }
452501
453502 if (! -T $file) {
503+ if ($file =~ m|^/sys/kernel/btf/| or
504+ $file =~ m|^/sys/devices/pci| or
505+ $file =~ m|^/sys/firmware/efi/efivars/| or
506+ $file =~ m|^/proc/bus/pci/|) {
507+ return;
508+ }
509+ if (scalar @kallsyms > 0) {
510+ parse_binary($file);
511+ }
454512 return;
455513 }
456514
457515 open my $fh, "<", $file or return;
458516 while ( <$fh> ) {
459517 chomp;
460− if (may_leak_address($_)) {
518+ if (may_leak_address($file, $_)) {
461519 printf("$file: $_\n");
462520 }
463521 }
@@ -469,7 +527,7 @@ sub check_path_for_leaks · 5 unmodified lines
469527 {
470528 my ($path) = @_;
471529
472− if (may_leak_address($path)) {
530+ if (may_leak_address($path, $path)) {
473531 printf("Path name may contain address: $path\n");
474532 }
475533 }