Commit 727dbda1 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull hardening updates from Kees Cook:
 "As has become normal, changes are scattered around the tree (either
  explicitly maintainer Acked or for trivial stuff that went ignored):

   - Carve out the new CONFIG_LIST_HARDENED as a more focused subset of
     CONFIG_DEBUG_LIST (Marco Elver)

   - Fix kallsyms lookup failure under Clang LTO (Yonghong Song)

   - Clarify documentation for CONFIG_UBSAN_TRAP (Jann Horn)

   - Flexible array member conversion not carried in other tree (Gustavo
     A. R. Silva)

   - Various strlcpy() and strncpy() removals not carried in other trees
     (Azeem Shaikh, Justin Stitt)

   - Convert nsproxy.count to refcount_t (Elena Reshetova)

   - Add handful of __counted_by annotations not carried in other trees,
     as well as an LKDTM test

   - Fix build failure with gcc-plugins on GCC 14+

   - Fix selftests to respect SKIP for signal-delivery tests

   - Fix CFI warning for paravirt callback prototype

   - Clarify documentation for seq_show_option_n() usage"

* tag 'hardening-v6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (23 commits)
  LoadPin: Annotate struct dm_verity_loadpin_trusted_root_digest with __counted_by
  kallsyms: Change func signature for cleanup_symbol_name()
  kallsyms: Fix kallsyms_selftest failure
  nsproxy: Convert nsproxy.count to refcount_t
  integrity: Annotate struct ima_rule_opt_list with __counted_by
  lkdtm: Add FAM_BOUNDS test for __counted_by
  Compiler Attributes: counted_by: Adjust name and identifier expansion
  um: refactor deprecated strncpy to memcpy
  um: vector: refactor deprecated strncpy
  alpha: Replace one-element array with flexible-array member
  hardening: Move BUG_ON_DATA_CORRUPTION to hardening options
  list: Introduce CONFIG_LIST_HARDENED
  list_debug: Introduce inline wrappers for debug checks
  compiler_types: Introduce the Clang __preserve_most function attribute
  gcc-plugins: Rename last_stmt() for GCC 14+
  selftests/harness: Actually report SKIP for signal tests
  x86/paravirt: Fix tlb_remove_table function callback prototype warning
  EISA: Replace all non-returning strlcpy with strscpy
  perf: Replace strlcpy with strscpy
  um: Remove strlcpy declaration
  ...
parents b03a4342 5f536ac6
......@@ -97,7 +97,7 @@ struct osf_dirent {
unsigned int d_ino;
unsigned short d_reclen;
unsigned short d_namlen;
char d_name[1];
char d_name[];
};
struct osf_dirent_callback {
......
......@@ -25,7 +25,7 @@ hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o
cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o
hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o
hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o
hyp-obj-$(CONFIG_LIST_HARDENED) += list_debug.o
hyp-obj-y += $(lib-objs)
##
......
......@@ -26,8 +26,9 @@ static inline __must_check bool nvhe_check_data_corruption(bool v)
/* The predicates checked here are taken from lib/list_debug.c. */
bool __list_add_valid(struct list_head *new, struct list_head *prev,
struct list_head *next)
__list_valid_slowpath
bool __list_add_valid_or_report(struct list_head *new, struct list_head *prev,
struct list_head *next)
{
if (NVHE_CHECK_DATA_CORRUPTION(next->prev != prev) ||
NVHE_CHECK_DATA_CORRUPTION(prev->next != next) ||
......@@ -37,7 +38,8 @@ bool __list_add_valid(struct list_head *new, struct list_head *prev,
return true;
}
bool __list_del_entry_valid(struct list_head *entry)
__list_valid_slowpath
bool __list_del_entry_valid_or_report(struct list_head *entry)
{
struct list_head *prev, *next;
......
......@@ -554,7 +554,7 @@ struct mconsole_output {
static DEFINE_SPINLOCK(client_lock);
static LIST_HEAD(clients);
static char console_buf[MCONSOLE_MAX_DATA];
static char console_buf[MCONSOLE_MAX_DATA] __nonstring;
static void console_write(struct console *console, const char *string,
unsigned int len)
......@@ -567,7 +567,7 @@ static void console_write(struct console *console, const char *string,
while (len > 0) {
n = min((size_t) len, ARRAY_SIZE(console_buf));
strncpy(console_buf, string, n);
memcpy(console_buf, string, n);
string += n;
len -= n;
......
......@@ -141,7 +141,7 @@ static int create_tap_fd(char *iface)
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
strscpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
err = ioctl(fd, TUNSETIFF, (void *) &ifr);
if (err != 0) {
......@@ -171,7 +171,7 @@ static int create_raw_fd(char *iface, int flags, int proto)
goto raw_fd_cleanup;
}
memset(&ifr, 0, sizeof(ifr));
strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
strscpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {
err = -errno;
goto raw_fd_cleanup;
......
......@@ -50,7 +50,6 @@ static inline int printk(const char *fmt, ...)
#endif
extern int in_aton(char *str);
extern size_t strlcpy(char *, const char *, size_t);
extern size_t strlcat(char *, const char *, size_t);
extern size_t strscpy(char *, const char *, size_t);
......
......@@ -40,7 +40,7 @@ static int __init make_uml_dir(void)
__func__);
goto err;
}
strlcpy(dir, home, sizeof(dir));
strscpy(dir, home, sizeof(dir));
uml_dir++;
}
strlcat(dir, uml_dir, sizeof(dir));
......@@ -243,7 +243,7 @@ int __init set_umid(char *name)
if (strlen(name) > UMID_LEN - 1)
return -E2BIG;
strlcpy(umid, name, sizeof(umid));
strscpy(umid, name, sizeof(umid));
return 0;
}
......@@ -262,7 +262,7 @@ static int __init make_umid(void)
make_uml_dir();
if (*umid == '\0') {
strlcpy(tmp, uml_dir, sizeof(tmp));
strscpy(tmp, uml_dir, sizeof(tmp));
strlcat(tmp, "XXXXXX", sizeof(tmp));
fd = mkstemp(tmp);
if (fd < 0) {
......
......@@ -79,6 +79,11 @@ void __init native_pv_lock_init(void)
static_branch_disable(&virt_spin_lock_key);
}
static void native_tlb_remove_table(struct mmu_gather *tlb, void *table)
{
tlb_remove_page(tlb, table);
}
unsigned int paravirt_patch(u8 type, void *insn_buff, unsigned long addr,
unsigned int len)
{
......@@ -295,8 +300,7 @@ struct paravirt_patch_template pv_ops = {
.mmu.flush_tlb_kernel = native_flush_tlb_global,
.mmu.flush_tlb_one_user = native_flush_tlb_one_user,
.mmu.flush_tlb_multi = native_flush_tlb_multi,
.mmu.tlb_remove_table =
(void (*)(struct mmu_gather *, void *))tlb_remove_page,
.mmu.tlb_remove_table = native_tlb_remove_table,
.mmu.exit_mmap = paravirt_nop,
.mmu.notify_page_enc_status_changed = paravirt_nop,
......
......@@ -60,7 +60,7 @@ static void __init eisa_name_device(struct eisa_device *edev)
int i;
for (i = 0; i < EISA_INFOS; i++) {
if (!strcmp(edev->id.sig, eisa_table[i].id.sig)) {
strlcpy(edev->pretty_name,
strscpy(edev->pretty_name,
eisa_table[i].name,
sizeof(edev->pretty_name));
return;
......
......@@ -273,8 +273,8 @@ static void lkdtm_HUNG_TASK(void)
schedule();
}
volatile unsigned int huge = INT_MAX - 2;
volatile unsigned int ignored;
static volatile unsigned int huge = INT_MAX - 2;
static volatile unsigned int ignored;
static void lkdtm_OVERFLOW_SIGNED(void)
{
......@@ -305,7 +305,7 @@ static void lkdtm_OVERFLOW_UNSIGNED(void)
ignored = value;
}
/* Intentionally using old-style flex array definition of 1 byte. */
/* Intentionally using unannotated flex array definition. */
struct array_bounds_flex_array {
int one;
int two;
......@@ -357,6 +357,46 @@ static void lkdtm_ARRAY_BOUNDS(void)
pr_expected_config(CONFIG_UBSAN_BOUNDS);
}
struct lkdtm_annotated {
unsigned long flags;
int count;
int array[] __counted_by(count);
};
static volatile int fam_count = 4;
static void lkdtm_FAM_BOUNDS(void)
{
struct lkdtm_annotated *inst;
inst = kzalloc(struct_size(inst, array, fam_count + 1), GFP_KERNEL);
if (!inst) {
pr_err("FAIL: could not allocate test struct!\n");
return;
}
inst->count = fam_count;
pr_info("Array access within bounds ...\n");
inst->array[1] = fam_count;
ignored = inst->array[1];
pr_info("Array access beyond bounds ...\n");
inst->array[fam_count] = fam_count;
ignored = inst->array[fam_count];
kfree(inst);
pr_err("FAIL: survived access of invalid flexible array member index!\n");
if (!__has_attribute(__counted_by__))
pr_warn("This is expected since this %s was built a compiler supporting __counted_by\n",
lkdtm_kernel_info);
else if (IS_ENABLED(CONFIG_UBSAN_BOUNDS))
pr_expected_config(CONFIG_UBSAN_TRAP);
else
pr_expected_config(CONFIG_UBSAN_BOUNDS);
}
static void lkdtm_CORRUPT_LIST_ADD(void)
{
/*
......@@ -393,7 +433,7 @@ static void lkdtm_CORRUPT_LIST_ADD(void)
pr_err("Overwrite did not happen, but no BUG?!\n");
else {
pr_err("list_add() corruption not detected!\n");
pr_expected_config(CONFIG_DEBUG_LIST);
pr_expected_config(CONFIG_LIST_HARDENED);
}
}
......@@ -420,7 +460,7 @@ static void lkdtm_CORRUPT_LIST_DEL(void)
pr_err("Overwrite did not happen, but no BUG?!\n");
else {
pr_err("list_del() corruption not detected!\n");
pr_expected_config(CONFIG_DEBUG_LIST);
pr_expected_config(CONFIG_LIST_HARDENED);
}
}
......@@ -616,6 +656,7 @@ static struct crashtype crashtypes[] = {
CRASHTYPE(OVERFLOW_SIGNED),
CRASHTYPE(OVERFLOW_UNSIGNED),
CRASHTYPE(ARRAY_BOUNDS),
CRASHTYPE(FAM_BOUNDS),
CRASHTYPE(CORRUPT_LIST_ADD),
CRASHTYPE(CORRUPT_LIST_DEL),
CRASHTYPE(STACK_GUARD_PAGE_LEADING),
......
......@@ -524,7 +524,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware)
* saved microcode information and put in the new.
*/
memset(&qe_firmware_info, 0, sizeof(qe_firmware_info));
strlcpy(qe_firmware_info.id, firmware->id, sizeof(qe_firmware_info.id));
strscpy(qe_firmware_info.id, firmware->id, sizeof(qe_firmware_info.id));
qe_firmware_info.extended_modes = be64_to_cpu(firmware->extended_modes);
memcpy(qe_firmware_info.vtraps, firmware->vtraps,
sizeof(firmware->vtraps));
......@@ -599,7 +599,7 @@ struct qe_firmware_info *qe_get_firmware_info(void)
/* Copy the data into qe_firmware_info*/
sprop = of_get_property(fw, "id", NULL);
if (sprop)
strlcpy(qe_firmware_info.id, sprop,
strscpy(qe_firmware_info.id, sprop,
sizeof(qe_firmware_info.id));
of_property_read_u64(fw, "extended-modes",
......
......@@ -94,6 +94,19 @@
# define __copy(symbol)
#endif
/*
* Optional: only supported since gcc >= 14
* Optional: only supported since clang >= 18
*
* gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
* clang: https://reviews.llvm.org/D148381
*/
#if __has_attribute(__counted_by__)
# define __counted_by(member) __attribute__((__counted_by__(member)))
#else
# define __counted_by(member)
#endif
/*
* Optional: not supported by gcc
* Optional: only supported since clang >= 14.0
......@@ -129,19 +142,6 @@
# define __designated_init
#endif
/*
* Optional: only supported since gcc >= 14
* Optional: only supported since clang >= 17
*
* gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
* clang: https://reviews.llvm.org/D148381
*/
#if __has_attribute(__element_count__)
# define __counted_by(member) __attribute__((__element_count__(#member)))
#else
# define __counted_by(member)
#endif
/*
* Optional: only supported since clang >= 14.0
*
......
......@@ -106,6 +106,34 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
#define __cold
#endif
/*
* On x86-64 and arm64 targets, __preserve_most changes the calling convention
* of a function to make the code in the caller as unintrusive as possible. This
* convention behaves identically to the C calling convention on how arguments
* and return values are passed, but uses a different set of caller- and callee-
* saved registers.
*
* The purpose is to alleviates the burden of saving and recovering a large
* register set before and after the call in the caller. This is beneficial for
* rarely taken slow paths, such as error-reporting functions that may be called
* from hot paths.
*
* Note: This may conflict with instrumentation inserted on function entry which
* does not use __preserve_most or equivalent convention (if in assembly). Since
* function tracing assumes the normal C calling convention, where the attribute
* is supported, __preserve_most implies notrace. It is recommended to restrict
* use of the attribute to functions that should or already disable tracing.
*
* Optional: not supported by gcc.
*
* clang: https://clang.llvm.org/docs/AttributeReference.html#preserve-most
*/
#if __has_attribute(__preserve_most__) && (defined(CONFIG_X86_64) || defined(CONFIG_ARM64))
# define __preserve_most notrace __attribute__((__preserve_most__))
#else
# define __preserve_most
#endif
/* Builtins */
/*
......
......@@ -12,7 +12,7 @@ extern struct list_head dm_verity_loadpin_trusted_root_digests;
struct dm_verity_loadpin_trusted_root_digest {
struct list_head node;
unsigned int len;
u8 data[];
u8 data[] __counted_by(len);
};
#if IS_ENABLED(CONFIG_SECURITY_LOADPIN_VERITY)
......
......@@ -38,11 +38,92 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
WRITE_ONCE(list->prev, list);
}
#ifdef CONFIG_LIST_HARDENED
#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next);
extern bool __list_del_entry_valid(struct list_head *entry);
# define __list_valid_slowpath
#else
# define __list_valid_slowpath __cold __preserve_most
#endif
/*
* Performs the full set of list corruption checks before __list_add().
* On list corruption reports a warning, and returns false.
*/
extern bool __list_valid_slowpath __list_add_valid_or_report(struct list_head *new,
struct list_head *prev,
struct list_head *next);
/*
* Performs list corruption checks before __list_add(). Returns false if a
* corruption is detected, true otherwise.
*
* With CONFIG_LIST_HARDENED only, performs minimal list integrity checking
* inline to catch non-faulting corruptions, and only if a corruption is
* detected calls the reporting function __list_add_valid_or_report().
*/
static __always_inline bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
bool ret = true;
if (!IS_ENABLED(CONFIG_DEBUG_LIST)) {
/*
* With the hardening version, elide checking if next and prev
* are NULL, since the immediate dereference of them below would
* result in a fault if NULL.
*
* With the reduced set of checks, we can afford to inline the
* checks, which also gives the compiler a chance to elide some
* of them completely if they can be proven at compile-time. If
* one of the pre-conditions does not hold, the slow-path will
* show a report which pre-condition failed.
*/
if (likely(next->prev == prev && prev->next == next && new != prev && new != next))
return true;
ret = false;
}
ret &= __list_add_valid_or_report(new, prev, next);
return ret;
}
/*
* Performs the full set of list corruption checks before __list_del_entry().
* On list corruption reports a warning, and returns false.
*/
extern bool __list_valid_slowpath __list_del_entry_valid_or_report(struct list_head *entry);
/*
* Performs list corruption checks before __list_del_entry(). Returns false if a
* corruption is detected, true otherwise.
*
* With CONFIG_LIST_HARDENED only, performs minimal list integrity checking
* inline to catch non-faulting corruptions, and only if a corruption is
* detected calls the reporting function __list_del_entry_valid_or_report().
*/
static __always_inline bool __list_del_entry_valid(struct list_head *entry)
{
bool ret = true;
if (!IS_ENABLED(CONFIG_DEBUG_LIST)) {
struct list_head *prev = entry->prev;
struct list_head *next = entry->next;
/*
* With the hardening version, elide checking if next and prev
* are NULL, LIST_POISON1 or LIST_POISON2, since the immediate
* dereference of them below would result in a fault.
*/
if (likely(prev->next == entry && next->prev == entry))
return true;
ret = false;
}
ret &= __list_del_entry_valid_or_report(entry);
return ret;
}
#else
static inline bool __list_add_valid(struct list_head *new,
struct list_head *prev,
......
......@@ -29,7 +29,7 @@ struct fs_struct;
* nsproxy is copied.
*/
struct nsproxy {
atomic_t count;
refcount_t count;
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
......@@ -102,14 +102,13 @@ int __init nsproxy_cache_init(void);
static inline void put_nsproxy(struct nsproxy *ns)
{
if (atomic_dec_and_test(&ns->count)) {
if (refcount_dec_and_test(&ns->count))
free_nsproxy(ns);
}
}
static inline void get_nsproxy(struct nsproxy *ns)
{
atomic_inc(&ns->count);
refcount_inc(&ns->count);
}
#endif
......@@ -249,18 +249,19 @@ static inline void seq_show_option(struct seq_file *m, const char *name,
/**
* seq_show_option_n - display mount options with appropriate escapes
* where @value must be a specific length.
* where @value must be a specific length (i.e.
* not NUL-terminated).
* @m: the seq_file handle
* @name: the mount option name
* @value: the mount option name's value, cannot be NULL
* @length: the length of @value to display
* @length: the exact length of @value to display, must be constant expression
*
* This is a macro since this uses "length" to define the size of the
* stack buffer.
*/
#define seq_show_option_n(m, name, value, length) { \
char val_buf[length + 1]; \
strncpy(val_buf, value, length); \
memcpy(val_buf, value, length); \
val_buf[length] = '\0'; \
seq_show_option(m, name, val_buf); \
}
......
......@@ -45,3 +45,7 @@
TYPE NAME[]; \
}
#endif
#ifndef __counted_by
#define __counted_by(m)
#endif
......@@ -8249,7 +8249,7 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event)
unsigned int size;
memset(comm, 0, sizeof(comm));
strlcpy(comm, comm_event->task->comm, sizeof(comm));
strscpy(comm, comm_event->task->comm, sizeof(comm));
size = ALIGN(strlen(comm)+1, sizeof(u64));
comm_event->comm = comm;
......@@ -8704,7 +8704,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
}
cpy_name:
strlcpy(tmp, name, sizeof(tmp));
strscpy(tmp, name, sizeof(tmp));
name = tmp;
got_name:
/*
......@@ -9128,7 +9128,7 @@ void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, bool unregister,
ksym_type == PERF_RECORD_KSYMBOL_TYPE_UNKNOWN)
goto err;
strlcpy(name, sym, KSYM_NAME_LEN);
strscpy(name, sym, KSYM_NAME_LEN);
name_len = strlen(name) + 1;
while (!IS_ALIGNED(name_len, sizeof(u64)))
name[name_len++] = '\0';
......
......@@ -163,12 +163,12 @@ unsigned long kallsyms_sym_address(int idx)
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}
static bool cleanup_symbol_name(char *s)
static void cleanup_symbol_name(char *s)
{
char *res;
if (!IS_ENABLED(CONFIG_LTO_CLANG))
return false;
return;
/*
* LLVM appends various suffixes for local functions and variables that
......@@ -178,26 +178,21 @@ static bool cleanup_symbol_name(char *s)
* - foo.llvm.[0-9a-f]+
*/
res = strstr(s, ".llvm.");
if (res) {
if (res)
*res = '\0';
return true;
}
return false;
return;
}
static int compare_symbol_name(const char *name, char *namebuf)
{
int ret;
ret = strcmp(name, namebuf);
if (!ret)
return ret;
if (cleanup_symbol_name(namebuf) && !strcmp(name, namebuf))
return 0;
return ret;
/* The kallsyms_seqs_of_names is sorted based on names after
* cleanup_symbol_name() (see scripts/kallsyms.c) if clang lto is enabled.
* To ensure correct bisection in kallsyms_lookup_names(), do
* cleanup_symbol_name(namebuf) before comparing name and namebuf.
*/
cleanup_symbol_name(namebuf);
return strcmp(name, namebuf);
}
static unsigned int get_symbol_seq(int index)
......
......@@ -196,7 +196,7 @@ static bool match_cleanup_name(const char *s, const char *name)
if (!IS_ENABLED(CONFIG_LTO_CLANG))
return false;
p = strchr(s, '.');
p = strstr(s, ".llvm.");
if (!p)
return false;
......@@ -344,27 +344,6 @@ static int test_kallsyms_basic_function(void)
goto failed;
}
/*
* The first '.' may be the initial letter, in which case the
* entire symbol name will be truncated to an empty string in
* cleanup_symbol_name(). Do not test these symbols.
*
* For example:
* cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head
* .E_read_words
* .E_leading_bytes
* .E_trailing_bytes
* .E_write_words
* .E_copy
* .str.292.llvm.12122243386960820698
* .str.24.llvm.12122243386960820698
* .str.29.llvm.12122243386960820698
* .str.75.llvm.12122243386960820698
* .str.99.llvm.12122243386960820698
*/
if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0])
continue;
lookup_addr = kallsyms_lookup_name(namebuf);
memset(stat, 0, sizeof(*stat));
......
......@@ -30,7 +30,7 @@
static struct kmem_cache *nsproxy_cachep;
struct nsproxy init_nsproxy = {
.count = ATOMIC_INIT(1),
.count = REFCOUNT_INIT(1),
.uts_ns = &init_uts_ns,
#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
.ipc_ns = &init_ipc_ns,
......@@ -55,7 +55,7 @@ static inline struct nsproxy *create_nsproxy(void)
nsproxy = kmem_cache_alloc(nsproxy_cachep, GFP_KERNEL);
if (nsproxy)
atomic_set(&nsproxy->count, 1);
refcount_set(&nsproxy->count, 1);
return nsproxy;
}
......
......@@ -1673,10 +1673,15 @@ menu "Debug kernel data structures"
config DEBUG_LIST
bool "Debug linked list manipulation"
depends on DEBUG_KERNEL || BUG_ON_DATA_CORRUPTION
depends on DEBUG_KERNEL
select LIST_HARDENED
help
Enable this to turn on extended checks in the linked-list
walking routines.
Enable this to turn on extended checks in the linked-list walking
routines.
This option trades better quality error reports for performance, and
is more suitable for kernel debugging. If you care about performance,
you should only enable CONFIG_LIST_HARDENED instead.
If unsure, say N.
......@@ -1710,16 +1715,6 @@ config DEBUG_NOTIFIERS
This is a relatively cheap check but if you care about maximum
performance, say N.
config BUG_ON_DATA_CORRUPTION
bool "Trigger a BUG when data corruption is detected"
select DEBUG_LIST
help
Select this option if the kernel should BUG when it encounters
data corruption in kernel memory structures when they get checked
for validity.
If unsure, say N.
config DEBUG_MAPLE_TREE
bool "Debug maple trees"
depends on DEBUG_KERNEL
......
......@@ -13,7 +13,7 @@ menuconfig UBSAN
if UBSAN
config UBSAN_TRAP
bool "On Sanitizer warnings, abort the running kernel code"
bool "Abort on Sanitizer warnings (smaller kernel but less verbose)"
depends on !COMPILE_TEST
help
Building kernels with Sanitizer features enabled tends to grow
......@@ -26,6 +26,14 @@ config UBSAN_TRAP
the system. For some system builders this is an acceptable
trade-off.
Also note that selecting Y will cause your kernel to Oops
with an "illegal instruction" error with no further details
when a UBSAN violation occurs. (Except on arm64, which will
report which Sanitizer failed.) This may make it hard to
determine whether an Oops was caused by UBSAN or to figure
out the details of a UBSAN violation. It makes the kernel log
output less useful for bug reports.
config CC_HAS_UBSAN_BOUNDS_STRICT
def_bool $(cc-option,-fsanitize=bounds-strict)
help
......
......@@ -167,7 +167,7 @@ obj-$(CONFIG_BTREE) += btree.o
obj-$(CONFIG_INTERVAL_TREE) += interval_tree.o
obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
obj-$(CONFIG_DEBUG_LIST) += list_debug.o
obj-$(CONFIG_LIST_HARDENED) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
obj-$(CONFIG_BITREVERSE) += bitrev.o
......
......@@ -2,7 +2,8 @@
* Copyright 2006, Red Hat, Inc., Dave Jones
* Released under the General Public License (GPL).
*
* This file contains the linked list validation for DEBUG_LIST.
* This file contains the linked list validation and error reporting for
* LIST_HARDENED and DEBUG_LIST.
*/
#include <linux/export.h>
......@@ -17,8 +18,9 @@
* attempt).
*/
bool __list_add_valid(struct list_head *new, struct list_head *prev,
struct list_head *next)
__list_valid_slowpath
bool __list_add_valid_or_report(struct list_head *new, struct list_head *prev,
struct list_head *next)
{
if (CHECK_DATA_CORRUPTION(prev == NULL,
"list_add corruption. prev is NULL.\n") ||
......@@ -37,9 +39,10 @@ bool __list_add_valid(struct list_head *new, struct list_head *prev,
return true;
}
EXPORT_SYMBOL(__list_add_valid);
EXPORT_SYMBOL(__list_add_valid_or_report);
bool __list_del_entry_valid(struct list_head *entry)
__list_valid_slowpath
bool __list_del_entry_valid_or_report(struct list_head *entry)
{
struct list_head *prev, *next;
......@@ -65,6 +68,5 @@ bool __list_del_entry_valid(struct list_head *entry)
return false;
return true;
}
EXPORT_SYMBOL(__list_del_entry_valid);
EXPORT_SYMBOL(__list_del_entry_valid_or_report);
......@@ -440,4 +440,8 @@ static inline void debug_gimple_stmt(const_gimple s)
#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode)
#endif
#if BUILDING_GCC_VERSION >= 14000
#define last_stmt(x) last_nondebug_stmt(x)
#endif
#endif
......@@ -279,6 +279,29 @@ config ZERO_CALL_USED_REGS
endmenu
menu "Hardening of kernel data structures"
config LIST_HARDENED
bool "Check integrity of linked list manipulation"
help
Minimal integrity checking in the linked-list manipulation routines
to catch memory corruptions that are not guaranteed to result in an
immediate access fault.
If unsure, say N.
config BUG_ON_DATA_CORRUPTION
bool "Trigger a BUG when data corruption is detected"
select LIST_HARDENED
help
Select this option if the kernel should BUG when it encounters
data corruption in kernel memory structures when they get checked
for validity.
If unsure, say N.
endmenu
config CC_HAS_RANDSTRUCT
def_bool $(cc-option,-frandomize-layout-seed-file=/dev/null)
# Randstruct was first added in Clang 15, but it isn't safe to use until
......
......@@ -68,7 +68,7 @@ enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
struct ima_rule_opt_list {
size_t count;
char *items[];
char *items[] __counted_by(count);
};
/*
......@@ -342,6 +342,7 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
kfree(src_copy);
return ERR_PTR(-ENOMEM);
}
opt_list->count = count;
/*
* strsep() has already replaced all instances of '|' with '\0',
......@@ -357,7 +358,6 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
opt_list->items[i] = cur;
cur = strchr(cur, '\0') + 1;
}
opt_list->count = count;
return opt_list;
}
......
......@@ -336,6 +336,7 @@ static int read_trusted_verity_root_digests(unsigned int fd)
rc = -ENOMEM;
goto err;
}
trd->len = len;
if (hex2bin(trd->data, d, len)) {
kfree(trd);
......@@ -343,8 +344,6 @@ static int read_trusted_verity_root_digests(unsigned int fd)
goto err;
}
trd->len = len;
list_add_tail(&trd->node, &dm_verity_loadpin_trusted_root_digests);
}
......
......@@ -938,7 +938,11 @@ void __wait_for_test(struct __test_metadata *t)
fprintf(TH_LOG_STREAM,
"# %s: Test terminated by timeout\n", t->name);
} else if (WIFEXITED(status)) {
if (t->termsig != -1) {
if (WEXITSTATUS(status) == 255) {
/* SKIP */
t->passed = 1;
t->skip = 1;
} else if (t->termsig != -1) {
t->passed = 0;
fprintf(TH_LOG_STREAM,
"# %s: Test exited normally instead of by signal (code: %d)\n",
......@@ -950,11 +954,6 @@ void __wait_for_test(struct __test_metadata *t)
case 0:
t->passed = 1;
break;
/* SKIP */
case 255:
t->passed = 1;
t->skip = 1;
break;
/* Other failure, assume step report. */
default:
t->passed = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment