mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-01 10:42:11 +00:00
hardening updates for v6.6-rc1
- 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. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmTs6ZAWHGtlZXNjb29r QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJkpjD/9AeST5Imc2t0t71Qd+wPxW3jT3 kDZPlHH8wHmuxSpRscX82m21SozvEMvybo6Cp7FSH4qr863FnBWMlo8acr7rKxUf 0f7Y9qgY/hKADiVx5p0pbnCgcy+l4pwsxIqVCGuhjvNCbWHrdGqLM4UjIfaVz5Ws +55a/C3S1KVwB1s1+6to43jtKqQAx6yrqYWOaT3wEfCzHC87f9PUHhIGnFQVwPGP WpjQI/BQKpH7+MDCoJOPrZqXaE/4lWALxR6+5BBheGbvLoWifpJEYHX6bDUzkgBz liQDkgr4eAw5EXSOS7mX3EApfeMKakznJt9Mcmn0h3pPRlM3ZSVD64Xrou2Brpje exS2JRuh6HwIiXY9nTHc6YMGcAWG1syAR/hM2fQdujM0CWtBUk9+kkuYWsqF6nIK 3tOxYLB/Ph4p+tShd+v5R3mEmp/6snYRKJoUk+9Fk67i54NnK4huyxaCO4zui+ML 3vHuGp8KgFHUjJaYmYXHs3TRZnKSFUkPGc4MbpiGtmJ9zhfSwlhhF+yfBJCsvmTf ZajA+sPupT4OjLxU6vUD/ZNkXAEjWzktyX2v9YBA7FHh7SqPtX9ARRIxh417AjEJ tBPHhW/iRw9ftBIAKDmI7gPLynngd/zvjhvk6O5egHYjjgRM1/WAJZ4V26XR6+hf TWfQb7VRzdZIqwOEUA== =9ZWP -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
727dbda16b
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user