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
175 175
176ifeq ($(CONFIG_UBSAN),y)176ifeq ($(CONFIG_UBSAN),y)
177_c_flags += $(if $(patsubst n%,, \177_c_flags += $(if $(patsubst n%,, \
178 $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \178 $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)y), \
179 $(CFLAGS_UBSAN))179 $(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))
180endif183endif
181 184
182ifeq ($(CONFIG_KCOV),y)185ifeq ($(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
10ubsan-cflags-$(CONFIG_UBSAN_UNREACHABLE) += -fsanitize=unreachable10ubsan-cflags-$(CONFIG_UBSAN_UNREACHABLE) += -fsanitize=unreachable
11ubsan-cflags-$(CONFIG_UBSAN_BOOL) += -fsanitize=bool11ubsan-cflags-$(CONFIG_UBSAN_BOOL) += -fsanitize=bool
12ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum12ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum
13ubsan-cflags-$(CONFIG_UBSAN_TRAP) += -fsanitize-undefined-trap-on-error13ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined,-fsanitize-undefined-trap-on-error)
14 14
15export CFLAGS_UBSAN := $(ubsan-cflags-y)15export CFLAGS_UBSAN := $(ubsan-cflags-y)
16
17ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
18export 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
6virtual patch
7virtual context
8virtual report
9
10@str_plural depends on patch@
11expression 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@
25expression E;
26position 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@
37p << str_plural_r.P;
38e << str_plural_r.E;
39@@
40
41coccilib.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
10virtual patch
11virtual context
12virtual org
13virtual 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@
19identifier f = overflow_size_helpers_test;
20@@
21
22f
23
24//----------------------------------------------------------
25// For context mode
26//----------------------------------------------------------
27
28@depends on !overflow_tests && context@
29expression E1, E2;
30identifier 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@
41expression E1, E2;
42identifier 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)@
54expression E1, E2;
55identifier m;
56position p;
57@@
58(
59 (sizeof(*E1)@p + (E2 * sizeof(*E1->m)))
60)
61
62@script:python depends on org@
63p << r.p;
64@@
65
66coccilib.org.print_todo(p[0], "WARNING should use struct_size")
67
68@script:python depends on report@
69p << r.p;
70@@
71
72msg="WARNING: Use struct_size"
73coccilib.report.print_report(p[0], msg)
74
scripts/leaking_addresses.pl+74−16
@@ -23,6 +23,7 @@ use strict; · 22 unmodified lines
23use POSIX;23use POSIX;
24use File::Basename;24use File::Basename;
25use File::Spec;25use File::Spec;
26use File::Temp qw/tempfile/;
26use Cwd 'abs_path';27use Cwd 'abs_path';
27use Term::ANSIColor qw(:constants);28use Term::ANSIColor qw(:constants);
28use Getopt::Long qw(:config no_auto_abbrev);29use 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
51my $suppress_dmesg = 0; # Don't show dmesg in output.52my $suppress_dmesg = 0; # Don't show dmesg in output.
52my $squash_by_path = 0; # Summary report grouped by absolute path.53my $squash_by_path = 0; # Summary report grouped by absolute path.
53my $squash_by_filename = 0; # Summary report grouped by filename.54my $squash_by_filename = 0; # Summary report grouped by filename.
55my $kallsyms_file = ""; # Kernel symbols file.
54my $kernel_config_file = ""; # Kernel configuration file.56my $kernel_config_file = ""; # Kernel configuration file.
55my $opt_32bit = 0; # Scan 32-bit kernel.57my $opt_32bit = 0; # Scan 32-bit kernel.
56my $page_offset_32bit = 0; # Page offset for 32-bit kernel.58my $page_offset_32bit = 0; # Page offset for 32-bit kernel.
57 59
60my @kallsyms = ();
61
58# Skip these absolute paths.62# Skip these absolute paths.
59my @skip_abs = (63my @skip_abs = (
60 '/proc/kmsg',64 '/proc/kmsg',
@@ -95,6 +99,8 @@ Options: · 34 unmodified lines
95 --squash-by-path Show one result per unique path.99 --squash-by-path Show one result per unique path.
96 --squash-by-filename Show one result per unique filename.100 --squash-by-filename Show one result per unique filename.
97 --kernel-config-file=<file> Kernel configuration file (e.g /boot/config)101 --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).
98 --32-bit Scan 32-bit kernel.104 --32-bit Scan 32-bit kernel.
99 --page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234).105 --page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234).
100 -d, --debug Display debugging output.106 -d, --debug Display debugging output.
@@ -115,6 +121,7 @@ GetOptions( · 14 unmodified lines
115 'squash-by-path' => \$squash_by_path,121 'squash-by-path' => \$squash_by_path,
116 'squash-by-filename' => \$squash_by_filename,122 'squash-by-filename' => \$squash_by_filename,
117 'raw' => \$raw,123 'raw' => \$raw,
124 'kallsyms=s' => \$kallsyms_file,
118 'kernel-config-file=s' => \$kernel_config_file,125 'kernel-config-file=s' => \$kernel_config_file,
119 '32-bit' => \$opt_32bit,126 '32-bit' => \$opt_32bit,
120 'page-offset-32-bit=o' => \$page_offset_32bit,127 'page-offset-32-bit=o' => \$page_offset_32bit,
@@ -155,6 +162,25 @@ if ($output_raw) { · 34 unmodified lines
155 select $fh;162 select $fh;
156}163}
157 164
165if ($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
158parse_dmesg();184parse_dmesg();
159walk(@DIRS);185walk(@DIRS);
160 186
@@ -221,6 +247,7 @@ sub get_kernel_config_option · 60 unmodified lines
221{247{
222 my ($option) = @_;248 my ($option) = @_;
223 my $value = "";249 my $value = "";
250 my $tmp_fh;
224 my $tmp_file = "";251 my $tmp_file = "";
225 my @config_files;252 my @config_files;
226 253
@@ -228,7 +255,8 @@ sub get_kernel_config_option · 1 unmodified line
228 if ($kernel_config_file ne "") {255 if ($kernel_config_file ne "") {
229 @config_files = ($kernel_config_file);256 @config_files = ($kernel_config_file);
230 } elsif (-R "/proc/config.gz") {257 } elsif (-R "/proc/config.gz") {
231 my $tmp_file = "/tmp/tmpkconf";258 ($tmp_fh, $tmp_file) = tempfile("config.gz-XXXXXX",
259 UNLINK => 1);
232 260
233 if (system("gunzip < /proc/config.gz > $tmp_file")) {261 if (system("gunzip < /proc/config.gz > $tmp_file")) {
234 dprint("system(gunzip < /proc/config.gz) failed\n");262 dprint("system(gunzip < /proc/config.gz) failed\n");
@@ -250,10 +278,6 @@ sub get_kernel_config_option · 15 unmodified lines
250 }278 }
251 }279 }
252 280
253 if ($tmp_file ne "") {
254 system("rm -f $tmp_file");
255 }
256
257 return $value;281 return $value;
258}282}
259 283
@@ -285,9 +309,10 @@ sub is_false_positive · 25 unmodified lines
285 return is_false_positive_32bit($match);309 return is_false_positive_32bit($match);
286 }310 }
287 311
288 # 64 bit false positives.312 # Ignore 64 bit false positives:
289 313 # 0xfffffffffffffff[0-f]
290 if ($match =~ '\b(0x)?(f|F){16}\b' or314 # 0x0000000000000000
315 if ($match =~ '\b(0x)?(f|F){15}[0-9a-f]\b' or
291 $match =~ '\b(0x)?0{16}\b') {316 $match =~ '\b(0x)?0{16}\b') {
292 return 1;317 return 1;
293 }318 }
@@ -304,7 +329,7 @@ sub is_false_positive_32bit · 10 unmodified lines
304 my ($match) = @_;329 my ($match) = @_;
305 state $page_offset = get_page_offset();330 state $page_offset = get_page_offset();
306 331
307 if ($match =~ '\b(0x)?(f|F){8}\b') {332 if ($match =~ '\b(0x)?(f|F){7}[0-9a-f]\b') {
308 return 1;333 return 1;
309 }334 }
310 335
@@ -347,18 +372,23 @@ sub is_in_vsyscall_memory_region · 36 unmodified lines
347# True if argument potentially contains a kernel address.372# True if argument potentially contains a kernel address.
348sub may_leak_address373sub may_leak_address
349{374{
350 my ($line) = @_;375 my ($path, $line) = @_;
351 my $address_re;376 my $address_re;
352 377
353 # Signal masks.378 # Ignore Signal masks.
354 if ($line =~ '^SigBlk:' or379 if ($line =~ '^SigBlk:' or
355 $line =~ '^SigIgn:' or380 $line =~ '^SigIgn:' or
356 $line =~ '^SigCgt:') {381 $line =~ '^SigCgt:') {
357 return 0;382 return 0;
358 }383 }
359 384
360 if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or385 # Ignore input device reporting.
361 $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {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')) {
362 return 0;392 return 0;
363 }393 }
364 394
@@ -401,7 +431,7 @@ sub parse_dmesg · 36 unmodified lines
401{431{
402 open my $cmd, '-|', 'dmesg';432 open my $cmd, '-|', 'dmesg';
403 while (<$cmd>) {433 while (<$cmd>) {
404 if (may_leak_address($_)) {434 if (may_leak_address("dmesg", $_)) {
405 print 'dmesg: ' . $_;435 print 'dmesg: ' . $_;
406 }436 }
407 }437 }
@@ -442,6 +472,25 @@ sub timed_parse_file · 34 unmodified lines
442 }472 }
443}473}
444 474
475sub 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
445sub parse_file494sub parse_file
446{495{
447 my ($file) = @_;496 my ($file) = @_;
@@ -451,13 +500,22 @@ sub parse_file · 3 unmodified lines
451 }500 }
452 501
453 if (! -T $file) {502 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 }
454 return;512 return;
455 }513 }
456 514
457 open my $fh, "<", $file or return;515 open my $fh, "<", $file or return;
458 while ( <$fh> ) {516 while ( <$fh> ) {
459 chomp;517 chomp;
460 if (may_leak_address($_)) {518 if (may_leak_address($file, $_)) {
461 printf("$file: $_\n");519 printf("$file: $_\n");
462 }520 }
463 }521 }
@@ -469,7 +527,7 @@ sub check_path_for_leaks · 5 unmodified lines
469{527{
470 my ($path) = @_;528 my ($path) = @_;
471 529
472 if (may_leak_address($path)) {530 if (may_leak_address($path, $path)) {
473 printf("Path name may contain address: $path\n");531 printf("Path name may contain address: $path\n");
474 }532 }
475}533}