summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-03-12 14:49:30 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-03-12 14:49:30 -0700
commit216532e147b2fee6ee830f4a844bbc3cbb9137af (patch)
tree0177c6fe41fcd036a8e20fe50ab03a982553fe97 /scripts
parentb32273ee89a866b01b316b9a8de407efde01090c (diff)
parent3fe1eb4dd2e4b872ffb7b9b081b34ffcfa934ba7 (diff)
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 ...
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.lib5
-rw-r--r--scripts/Makefile.ubsan5
-rw-r--r--scripts/coccinelle/api/string_choices.cocci41
-rw-r--r--scripts/coccinelle/misc/struct_size.cocci74
-rwxr-xr-xscripts/leaking_addresses.pl90
5 files changed, 197 insertions, 18 deletions
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 01a80a216f56..1bd59b8db05f 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -175,8 +175,11 @@ endif
ifeq ($(CONFIG_UBSAN),y)
_c_flags += $(if $(patsubst n%,, \
- $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)$(CONFIG_UBSAN_SANITIZE_ALL)), \
+ $(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SANITIZE)y), \
$(CFLAGS_UBSAN))
+_c_flags += $(if $(patsubst n%,, \
+ $(UBSAN_SIGNED_WRAP_$(basetarget).o)$(UBSAN_SANITIZE_$(basetarget).o)$(UBSAN_SIGNED_WRAP)$(UBSAN_SANITIZE)y), \
+ $(CFLAGS_UBSAN_SIGNED_WRAP))
endif
ifeq ($(CONFIG_KCOV),y)
diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan
index 4749865c1b2c..b2d3b273b802 100644
--- a/scripts/Makefile.ubsan
+++ b/scripts/Makefile.ubsan
@@ -10,6 +10,9 @@ ubsan-cflags-$(CONFIG_UBSAN_DIV_ZERO) += -fsanitize=integer-divide-by-zero
ubsan-cflags-$(CONFIG_UBSAN_UNREACHABLE) += -fsanitize=unreachable
ubsan-cflags-$(CONFIG_UBSAN_BOOL) += -fsanitize=bool
ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum
-ubsan-cflags-$(CONFIG_UBSAN_TRAP) += -fsanitize-undefined-trap-on-error
+ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(call cc-option,-fsanitize-trap=undefined,-fsanitize-undefined-trap-on-error)
export CFLAGS_UBSAN := $(ubsan-cflags-y)
+
+ubsan-signed-wrap-cflags-$(CONFIG_UBSAN_SIGNED_WRAP) += -fsanitize=signed-integer-overflow
+export CFLAGS_UBSAN_SIGNED_WRAP := $(ubsan-signed-wrap-cflags-y)
diff --git a/scripts/coccinelle/api/string_choices.cocci b/scripts/coccinelle/api/string_choices.cocci
new file mode 100644
index 000000000000..a71966c0494e
--- /dev/null
+++ b/scripts/coccinelle/api/string_choices.cocci
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/// Find places to use string_choices.h's various helpers.
+//
+// Confidence: Medium
+// Options: --no-includes --include-headers
+virtual patch
+virtual context
+virtual report
+
+@str_plural depends on patch@
+expression E;
+@@
+(
+- ((E == 1) ? "" : "s")
++ str_plural(E)
+|
+- ((E != 1) ? "s" : "")
++ str_plural(E)
+|
+- ((E > 1) ? "s" : "")
++ str_plural(E)
+)
+
+@str_plural_r depends on !patch exists@
+expression E;
+position P;
+@@
+(
+* ((E@P == 1) ? "" : "s")
+|
+* ((E@P != 1) ? "s" : "")
+|
+* ((E@P > 1) ? "s" : "")
+)
+
+@script:python depends on report@
+p << str_plural_r.P;
+e << str_plural_r.E;
+@@
+
+coccilib.report.print_report(p[0], "opportunity for str_plural(%s)" % e)
diff --git a/scripts/coccinelle/misc/struct_size.cocci b/scripts/coccinelle/misc/struct_size.cocci
new file mode 100644
index 000000000000..9b02c37438e4
--- /dev/null
+++ b/scripts/coccinelle/misc/struct_size.cocci
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+///
+/// Check for code that could use struct_size().
+///
+// Confidence: Medium
+// Author: Jacob Keller <jacob.e.keller@intel.com>
+// Copyright: (C) 2023 Intel Corporation
+// Options: --no-includes --include-headers
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+// the overflow Kunit tests have some code which intentionally does not use
+// the macros, so we want to ignore this code when reporting potential
+// issues.
+@overflow_tests@
+identifier f = overflow_size_helpers_test;
+@@
+
+f
+
+//----------------------------------------------------------
+// For context mode
+//----------------------------------------------------------
+
+@depends on !overflow_tests && context@
+expression E1, E2;
+identifier m;
+@@
+(
+* (sizeof(*E1) + (E2 * sizeof(*E1->m)))
+)
+
+//----------------------------------------------------------
+// For patch mode
+//----------------------------------------------------------
+
+@depends on !overflow_tests && patch@
+expression E1, E2;
+identifier m;
+@@
+(
+- (sizeof(*E1) + (E2 * sizeof(*E1->m)))
++ struct_size(E1, m, E2)
+)
+
+//----------------------------------------------------------
+// For org and report mode
+//----------------------------------------------------------
+
+@r depends on !overflow_tests && (org || report)@
+expression E1, E2;
+identifier m;
+position p;
+@@
+(
+ (sizeof(*E1)@p + (E2 * sizeof(*E1->m)))
+)
+
+@script:python depends on org@
+p << r.p;
+@@
+
+coccilib.org.print_todo(p[0], "WARNING should use struct_size")
+
+@script:python depends on report@
+p << r.p;
+@@
+
+msg="WARNING: Use struct_size"
+coccilib.report.print_report(p[0], msg)
+
diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl
index e695634d153d..8e992b18bcd9 100755
--- a/scripts/leaking_addresses.pl
+++ b/scripts/leaking_addresses.pl
@@ -23,6 +23,7 @@ use strict;
use POSIX;
use File::Basename;
use File::Spec;
+use File::Temp qw/tempfile/;
use Cwd 'abs_path';
use Term::ANSIColor qw(:constants);
use Getopt::Long qw(:config no_auto_abbrev);
@@ -51,10 +52,13 @@ my $input_raw = ""; # Read raw results from file instead of scanning.
my $suppress_dmesg = 0; # Don't show dmesg in output.
my $squash_by_path = 0; # Summary report grouped by absolute path.
my $squash_by_filename = 0; # Summary report grouped by filename.
+my $kallsyms_file = ""; # Kernel symbols file.
my $kernel_config_file = ""; # Kernel configuration file.
my $opt_32bit = 0; # Scan 32-bit kernel.
my $page_offset_32bit = 0; # Page offset for 32-bit kernel.
+my @kallsyms = ();
+
# Skip these absolute paths.
my @skip_abs = (
'/proc/kmsg',
@@ -95,6 +99,8 @@ Options:
--squash-by-path Show one result per unique path.
--squash-by-filename Show one result per unique filename.
--kernel-config-file=<file> Kernel configuration file (e.g /boot/config)
+ --kallsyms=<file> Read kernel symbol addresses from file (for
+ scanning binary files).
--32-bit Scan 32-bit kernel.
--page-offset-32-bit=o Page offset (for 32-bit kernel 0xABCD1234).
-d, --debug Display debugging output.
@@ -115,6 +121,7 @@ GetOptions(
'squash-by-path' => \$squash_by_path,
'squash-by-filename' => \$squash_by_filename,
'raw' => \$raw,
+ 'kallsyms=s' => \$kallsyms_file,
'kernel-config-file=s' => \$kernel_config_file,
'32-bit' => \$opt_32bit,
'page-offset-32-bit=o' => \$page_offset_32bit,
@@ -155,6 +162,25 @@ if ($output_raw) {
select $fh;
}
+if ($kallsyms_file) {
+ open my $fh, '<', $kallsyms_file or die "$0: $kallsyms_file: $!\n";
+ while (<$fh>) {
+ chomp;
+ my @entry = split / /, $_;
+ my $addr_text = $entry[0];
+ if ($addr_text !~ /^0/) {
+ # TODO: Why is hex() so impossibly slow?
+ my $addr = hex($addr_text);
+ my $symbol = $entry[2];
+ # Only keep kernel text addresses.
+ my $long = pack("J", $addr);
+ my $entry = [$long, $symbol];
+ push @kallsyms, $entry;
+ }
+ }
+ close $fh;
+}
+
parse_dmesg();
walk(@DIRS);
@@ -221,6 +247,7 @@ sub get_kernel_config_option
{
my ($option) = @_;
my $value = "";
+ my $tmp_fh;
my $tmp_file = "";
my @config_files;
@@ -228,7 +255,8 @@ sub get_kernel_config_option
if ($kernel_config_file ne "") {
@config_files = ($kernel_config_file);
} elsif (-R "/proc/config.gz") {
- my $tmp_file = "/tmp/tmpkconf";
+ ($tmp_fh, $tmp_file) = tempfile("config.gz-XXXXXX",
+ UNLINK => 1);
if (system("gunzip < /proc/config.gz > $tmp_file")) {
dprint("system(gunzip < /proc/config.gz) failed\n");
@@ -250,10 +278,6 @@ sub get_kernel_config_option
}
}
- if ($tmp_file ne "") {
- system("rm -f $tmp_file");
- }
-
return $value;
}
@@ -285,9 +309,10 @@ sub is_false_positive
return is_false_positive_32bit($match);
}
- # 64 bit false positives.
-
- if ($match =~ '\b(0x)?(f|F){16}\b' or
+ # Ignore 64 bit false positives:
+ # 0xfffffffffffffff[0-f]
+ # 0x0000000000000000
+ if ($match =~ '\b(0x)?(f|F){15}[0-9a-f]\b' or
$match =~ '\b(0x)?0{16}\b') {
return 1;
}
@@ -304,7 +329,7 @@ sub is_false_positive_32bit
my ($match) = @_;
state $page_offset = get_page_offset();
- if ($match =~ '\b(0x)?(f|F){8}\b') {
+ if ($match =~ '\b(0x)?(f|F){7}[0-9a-f]\b') {
return 1;
}
@@ -347,18 +372,23 @@ sub is_in_vsyscall_memory_region
# True if argument potentially contains a kernel address.
sub may_leak_address
{
- my ($line) = @_;
+ my ($path, $line) = @_;
my $address_re;
- # Signal masks.
+ # Ignore Signal masks.
if ($line =~ '^SigBlk:' or
$line =~ '^SigIgn:' or
$line =~ '^SigCgt:') {
return 0;
}
- if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
- $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') {
+ # Ignore input device reporting.
+ # /proc/bus/input/devices: B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
+ # /sys/devices/platform/i8042/serio0/input/input1/uevent: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
+ # /sys/devices/platform/i8042/serio0/input/input1/capabilities/key: 402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
+ if ($line =~ '\bKEY=[[:xdigit:]]{9,14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or
+ ($path =~ '\bkey$' and
+ $line =~ '\b[[:xdigit:]]{9,14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b')) {
return 0;
}
@@ -401,7 +431,7 @@ sub parse_dmesg
{
open my $cmd, '-|', 'dmesg';
while (<$cmd>) {
- if (may_leak_address($_)) {
+ if (may_leak_address("dmesg", $_)) {
print 'dmesg: ' . $_;
}
}
@@ -442,6 +472,25 @@ sub timed_parse_file
}
}
+sub parse_binary
+{
+ my ($file) = @_;
+
+ open my $fh, "<:raw", $file or return;
+ local $/ = undef;
+ my $bytes = <$fh>;
+ close $fh;
+
+ foreach my $entry (@kallsyms) {
+ my $addr = $entry->[0];
+ my $symbol = $entry->[1];
+ my $offset = index($bytes, $addr);
+ if ($offset != -1) {
+ printf("$file: $symbol @ $offset\n");
+ }
+ }
+}
+
sub parse_file
{
my ($file) = @_;
@@ -451,13 +500,22 @@ sub parse_file
}
if (! -T $file) {
+ if ($file =~ m|^/sys/kernel/btf/| or
+ $file =~ m|^/sys/devices/pci| or
+ $file =~ m|^/sys/firmware/efi/efivars/| or
+ $file =~ m|^/proc/bus/pci/|) {
+ return;
+ }
+ if (scalar @kallsyms > 0) {
+ parse_binary($file);
+ }
return;
}
open my $fh, "<", $file or return;
while ( <$fh> ) {
chomp;
- if (may_leak_address($_)) {
+ if (may_leak_address($file, $_)) {
printf("$file: $_\n");
}
}
@@ -469,7 +527,7 @@ sub check_path_for_leaks
{
my ($path) = @_;
- if (may_leak_address($path)) {
+ if (may_leak_address($path, $path)) {
printf("Path name may contain address: $path\n");
}
}