Commit 216532e1 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents b32273ee 3fe1eb4d
......@@ -49,34 +49,22 @@ Report example
Usage
-----
To enable UBSAN configure kernel with::
To enable UBSAN, configure the kernel with::
CONFIG_UBSAN=y
CONFIG_UBSAN=y
and to check the entire kernel::
CONFIG_UBSAN_SANITIZE_ALL=y
To enable instrumentation for specific files or directories, add a line
similar to the following to the respective kernel Makefile:
- For a single file (e.g. main.o)::
UBSAN_SANITIZE_main.o := y
- For all files in one directory::
UBSAN_SANITIZE := y
To exclude files from being instrumented even if
``CONFIG_UBSAN_SANITIZE_ALL=y``, use::
To exclude files from being instrumented use::
UBSAN_SANITIZE_main.o := n
and::
and to exclude all targets in one directory use::
UBSAN_SANITIZE := n
When disabled for all targets, specific files can be enabled using::
UBSAN_SANITIZE_main.o := y
Detection of unaligned accesses controlled through the separate option -
CONFIG_UBSAN_ALIGNMENT. It's off by default on architectures that support
unaligned accesses (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y). One could
......
......@@ -8989,9 +8989,10 @@ F: include/linux/string.h
F: include/linux/string_choices.h
F: include/linux/string_helpers.h
F: lib/string.c
F: lib/string_kunit.c
F: lib/string_helpers.c
F: lib/test-string_helpers.c
F: lib/test_string.c
F: lib/string_helpers_kunit.c
F: scripts/coccinelle/api/string_choices.cocci
GENERIC UIO DRIVER FOR PCI DEVICES
M: "Michael S. Tsirkin" <mst@redhat.com>
......@@ -12178,11 +12179,11 @@ F: Documentation/scsi/53c700.rst
F: drivers/scsi/53c700*
LEAKING_ADDRESSES
M: Tobin C. Harding <me@tobin.cc>
M: Tycho Andersen <tycho@tycho.pizza>
R: Kees Cook <keescook@chromium.org>
L: linux-hardening@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tobin/leaks.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: scripts/leaking_addresses.pl
LED SUBSYSTEM
......@@ -22503,6 +22504,23 @@ F: Documentation/block/ublk.rst
F: drivers/block/ublk_drv.c
F: include/uapi/linux/ublk_cmd.h
UBSAN
M: Kees Cook <keescook@chromium.org>
R: Marco Elver <elver@google.com>
R: Andrey Konovalov <andreyknvl@gmail.com>
R: Andrey Ryabinin <ryabinin.a.a@gmail.com>
L: kasan-dev@googlegroups.com
L: linux-hardening@vger.kernel.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
F: Documentation/dev-tools/ubsan.rst
F: include/linux/ubsan.h
F: lib/Kconfig.ubsan
F: lib/test_ubsan.c
F: lib/ubsan.c
F: scripts/Makefile.ubsan
K: \bARCH_HAS_UBSAN\b
UCLINUX (M68KNOMMU AND COLDFIRE)
M: Greg Ungerer <gerg@linux-m68k.org>
L: linux-m68k@lists.linux-m68k.org
......
......@@ -29,7 +29,7 @@ config ARM
select ARCH_HAVE_NMI_SAFE_CMPXCHG if CPU_V7 || CPU_V7M || CPU_V6K
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_KEEP_MEMBLOCK
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT if CPU_V7
......
......@@ -154,7 +154,7 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
putstr(" done, booting the kernel.\n");
}
void fortify_panic(const char *name)
void __fortify_panic(const u8 reason, size_t avail, size_t size)
{
error("detected buffer overflow");
}
......@@ -10,7 +10,7 @@ void __div0(void);
void
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
unsigned long free_mem_ptr_end_p, int arch_id);
void fortify_panic(const char *name);
void __fortify_panic(const u8 reason, size_t avail, size_t size);
int atags_to_fdt(void *atag_list, void *fdt, int total_space);
uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt);
int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x));
......
......@@ -8,7 +8,8 @@
* Little-endian word-at-a-time zero byte handling.
* Heavily based on the x86 algorithm.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
......
......@@ -107,7 +107,7 @@ config ARM64
select ARCH_WANT_LD_ORPHAN_WARN
select ARCH_WANTS_NO_INSTR
select ARCH_WANTS_THP_SWAP if ARM64_4K_PAGES
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARM_AMBA
select ARM_ARCH_TIMER
select ARM_GIC
......
......@@ -9,7 +9,8 @@
#ifndef __AARCH64EB__
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
......
......@@ -14,7 +14,7 @@ config MIPS
select ARCH_HAS_STRNCPY_FROM_USER
select ARCH_HAS_STRNLEN_USER
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_KEEP_MEMBLOCK
select ARCH_USE_BUILTIN_BSWAP
......
......@@ -12,7 +12,7 @@ config PARISC
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAS_PTE_SPECIAL
select ARCH_NO_SG_CHAIN
select ARCH_SUPPORTS_HUGETLBFS if PA20
......
......@@ -154,7 +154,7 @@ config PPC
select ARCH_HAS_SYSCALL_WRAPPER if !SPU_BASE && !COMPAT
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAS_UACCESS_FLUSHCACHE
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_KEEP_MEMBLOCK
select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE if PPC_RADIX_MMU
......
......@@ -4,8 +4,8 @@
/*
* Word-at-a-time interfaces for PowerPC.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
#include <asm/asm-compat.h>
#include <asm/extable.h>
......
......@@ -37,7 +37,7 @@ config RISCV
select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL
select ARCH_HAS_SYSCALL_WRAPPER
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAS_VDSO_DATA
select ARCH_KEEP_MEMBLOCK if ACPI
select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
......
......@@ -10,7 +10,8 @@
#include <asm/asm-extable.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
struct word_at_a_time {
const unsigned long one_bits, high_bits;
......
......@@ -82,7 +82,7 @@ config S390
select ARCH_HAS_STRICT_KERNEL_RWX
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_SYSCALL_WRAPPER
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAS_VDSO_DATA
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_INLINE_READ_LOCK
......
......@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
#include <asm/asm-extable.h>
#include <asm/bitsperlong.h>
......
......@@ -12,6 +12,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 \
vmlinux.bin.lzma vmlinux.bin.xz vmlinux.bin.lzo $(OBJECTS)
GCOV_PROFILE := n
UBSAN_SANITIZE := n
#
# IMAGE_OFFSET is the load offset of the compression loader
......
......@@ -5,6 +5,8 @@
#ifdef CONFIG_CPU_BIG_ENDIAN
# include <asm-generic/word-at-a-time.h>
#else
#include <linux/bitops.h>
#include <linux/wordpart.h>
/*
* Little-endian version cribbed from x86.
*/
......
......@@ -2,6 +2,7 @@
#
# Building vDSO images for sparc.
#
UBSAN_SANITIZE := n
# files to link into the vdso
vobjs-y := vdso-note.o vclock_gettime.o
......
......@@ -265,7 +265,7 @@ static void uml_net_poll_controller(struct net_device *dev)
static void uml_net_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, DRIVER_NAME, sizeof(info->driver));
strscpy(info->driver, DRIVER_NAME);
}
static const struct ethtool_ops uml_net_ethtool_ops = {
......
......@@ -1373,7 +1373,7 @@ static void vector_net_poll_controller(struct net_device *dev)
static void vector_net_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, DRIVER_NAME, sizeof(info->driver));
strscpy(info->driver, DRIVER_NAME);
}
static int vector_net_load_bpf_flash(struct net_device *dev,
......
......@@ -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;
strscpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
strscpy(ifr.ifr_name, iface);
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));
strscpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
strscpy(ifr.ifr_name, iface);
if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {
err = -errno;
goto raw_fd_cleanup;
......
......@@ -51,7 +51,8 @@ static inline int printk(const char *fmt, ...)
extern int in_aton(char *str);
extern size_t strlcat(char *, const char *, size_t);
extern size_t strscpy(char *, const char *, size_t);
extern size_t sized_strscpy(char *, const char *, size_t);
#define strscpy(dst, src) sized_strscpy(dst, src, sizeof(dst))
/* Copied from linux/compiler-gcc.h since we can't include it directly */
#define barrier() __asm__ __volatile__("": : :"memory")
......
......@@ -105,7 +105,7 @@ static int etap_tramp(char *dev, char *gate, int control_me,
sprintf(data_fd_buf, "%d", data_remote);
sprintf(version_buf, "%d", UML_NET_VERSION);
if (gate != NULL) {
strscpy(gate_buf, gate, sizeof(gate_buf));
strscpy(gate_buf, gate);
args = setup_args;
}
else args = nosetup_args;
......
......@@ -146,7 +146,7 @@ static int tuntap_open(void *data)
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strscpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name));
strscpy(ifr.ifr_name, pri->dev_name);
if (ioctl(pri->fd, TUNSETIFF, &ifr) < 0) {
err = -errno;
printk(UM_KERN_ERR "TUNSETIFF failed, errno = %d\n",
......
......@@ -40,7 +40,7 @@ static int __init make_uml_dir(void)
__func__);
goto err;
}
strscpy(dir, home, sizeof(dir));
strscpy(dir, home);
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;
strscpy(umid, name, sizeof(umid));
strscpy(umid, name);
return 0;
}
......@@ -262,7 +262,7 @@ static int __init make_umid(void)
make_uml_dir();
if (*umid == '\0') {
strscpy(tmp, uml_dir, sizeof(tmp));
strscpy(tmp, uml_dir);
strlcat(tmp, "XXXXXX", sizeof(tmp));
fd = mkstemp(tmp);
if (fd < 0) {
......
......@@ -100,7 +100,7 @@ config X86
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE
select ARCH_HAS_SYSCALL_WRAPPER
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARCH_HAS_UBSAN
select ARCH_HAS_DEBUG_WX
select ARCH_HAS_ZONE_DMA_SET if EXPERT
select ARCH_HAVE_NMI_SAFE_CMPXCHG
......
......@@ -531,7 +531,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output)
return output + entry_offset;
}
void fortify_panic(const char *name)
void __fortify_panic(const u8 reason, size_t avail, size_t size)
{
error("detected buffer overflow");
}
......@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
/*
* This is largely generic for little-endian machines, but the
......
......@@ -47,6 +47,7 @@
#include <linux/kern_levels.h>
#include <linux/kstrtox.h>
#include <linux/kthread.h>
#include <linux/wordpart.h>
#include <asm/page.h>
#include <asm/memtype.h>
......
......@@ -653,6 +653,14 @@ static void print_absolute_relocs(void)
if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
continue;
}
/*
* Do not perform relocations in .notes section; any
* values there are meant for pre-boot consumption (e.g.
* startup_xen).
*/
if (sec_applies->shdr.sh_type == SHT_NOTE) {
continue;
}
sh_symtab = sec_symtab->symtab;
sym_strtab = sec_symtab->link->strtab;
for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
......
......@@ -294,10 +294,11 @@ static void lkdtm_SPINLOCKUP(void)
__release(&lock_me_up);
}
static void lkdtm_HUNG_TASK(void)
static void __noreturn lkdtm_HUNG_TASK(void)
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
BUG();
}
static volatile unsigned int huge = INT_MAX - 2;
......
......@@ -153,12 +153,17 @@ static const struct crashtype *find_crashtype(const char *name)
/*
* This is forced noinline just so it distinctly shows up in the stackdump
* which makes validation of expected lkdtm crashes easier.
*
* NOTE: having a valid return value helps prevent the compiler from doing
* tail call optimizations and taking this out of the stack trace.
*/
static noinline void lkdtm_do_action(const struct crashtype *crashtype)
static noinline int lkdtm_do_action(const struct crashtype *crashtype)
{
if (WARN_ON(!crashtype || !crashtype->func))
return;
return -EINVAL;
crashtype->func();
return 0;
}
static int lkdtm_register_cpoint(struct crashpoint *crashpoint,
......@@ -167,10 +172,8 @@ static int lkdtm_register_cpoint(struct crashpoint *crashpoint,
int ret;
/* If this doesn't have a symbol, just call immediately. */
if (!crashpoint->kprobe.symbol_name) {
lkdtm_do_action(crashtype);
return 0;
}
if (!crashpoint->kprobe.symbol_name)
return lkdtm_do_action(crashtype);
if (lkdtm_kprobe != NULL)
unregister_kprobe(lkdtm_kprobe);
......@@ -216,7 +219,7 @@ static int lkdtm_kprobe_handler(struct kprobe *kp, struct pt_regs *regs)
spin_unlock_irqrestore(&crash_count_lock, flags);
if (do_it)
lkdtm_do_action(lkdtm_crashtype);
return lkdtm_do_action(lkdtm_crashtype);
return 0;
}
......@@ -303,6 +306,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
{
const struct crashtype *crashtype;
char *buf;
int err;
if (count >= PAGE_SIZE)
return -EINVAL;
......@@ -326,9 +330,11 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
return -EINVAL;
pr_info("Performing direct entry %s\n", crashtype->name);
lkdtm_do_action(crashtype);
err = lkdtm_do_action(crashtype);
*off += count;
if (err)
return err;
return count;
}
......
......@@ -224,8 +224,8 @@ static int dg_dispatch_as_host(u32 context_id, struct vmci_datagram *dg)
return VMCI_ERROR_NO_MEM;
}
dg_info = kmalloc(sizeof(*dg_info) +
(size_t) dg->payload_size, GFP_ATOMIC);
dg_info = kmalloc(struct_size(dg_info, msg_payload, dg->payload_size),
GFP_ATOMIC);
if (!dg_info) {
atomic_dec(&delayed_dg_host_queue_size);
vmci_resource_put(resource);
......@@ -234,7 +234,8 @@ static int dg_dispatch_as_host(u32 context_id, struct vmci_datagram *dg)
dg_info->in_dg_host_queue = true;
dg_info->entry = dst_entry;
memcpy(&dg_info->msg, dg, dg_size);
dg_info->msg = *dg;
memcpy(&dg_info->msg_payload, dg + 1, dg->payload_size);
INIT_WORK(&dg_info->work, dg_delayed_dispatch);
schedule_work(&dg_info->work);
......@@ -377,7 +378,8 @@ int vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg)
dg_info->in_dg_host_queue = false;
dg_info->entry = dst_entry;
memcpy(&dg_info->msg, dg, VMCI_DG_SIZE(dg));
dg_info->msg = *dg;
memcpy(&dg_info->msg_payload, dg + 1, dg->payload_size);
INIT_WORK(&dg_info->work, dg_delayed_dispatch);
schedule_work(&dg_info->work);
......
......@@ -17,8 +17,8 @@
#include <linux/init.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/wordpart.h>
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/namei.h>
......
......@@ -2,7 +2,8 @@
#ifndef _ASM_WORD_AT_A_TIME_H
#define _ASM_WORD_AT_A_TIME_H
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/wordpart.h>
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN
......
......@@ -231,6 +231,45 @@ static inline void *offset_to_ptr(const int *off)
* This returns a constant expression while determining if an argument is
* a constant expression, most importantly without evaluating the argument.
* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
*
* Details:
* - sizeof() return an integer constant expression, and does not evaluate
* the value of its operand; it only examines the type of its operand.
* - The results of comparing two integer constant expressions is also
* an integer constant expression.
* - The first literal "8" isn't important. It could be any literal value.
* - The second literal "8" is to avoid warnings about unaligned pointers;
* this could otherwise just be "1".
* - (long)(x) is used to avoid warnings about 64-bit types on 32-bit
* architectures.
* - The C Standard defines "null pointer constant", "(void *)0", as
* distinct from other void pointers.
* - If (x) is an integer constant expression, then the "* 0l" resolves
* it into an integer constant expression of value 0. Since it is cast to
* "void *", this makes the second operand a null pointer constant.
* - If (x) is not an integer constant expression, then the second operand
* resolves to a void pointer (but not a null pointer constant: the value
* is not an integer constant 0).
* - The conditional operator's third operand, "(int *)8", is an object
* pointer (to type "int").
* - The behavior (including the return type) of the conditional operator
* ("operand1 ? operand2 : operand3") depends on the kind of expressions
* given for the second and third operands. This is the central mechanism
* of the macro:
* - When one operand is a null pointer constant (i.e. when x is an integer
* constant expression) and the other is an object pointer (i.e. our
* third operand), the conditional operator returns the type of the
* object pointer operand (i.e. "int *). Here, within the sizeof(), we
* would then get:
* sizeof(*((int *)(...)) == sizeof(int) == 4
* - When one operand is a void pointer (i.e. when x is not an integer
* constant expression) and the other is an object pointer (i.e. our
* third operand), the conditional operator returns a "void *" type.
* Here, within the sizeof(), we would then get:
* sizeof(*((void *)(...)) == sizeof(void) == 1
* - The equality comparison to "sizeof(int)" therefore depends on (x):
* sizeof(int) == sizeof(int) (x) was a constant expression
* sizeof(int) != sizeof(void) (x) was not a constant expression
*/
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
......
......@@ -282,11 +282,18 @@ struct ftrace_likely_data {
#define __no_sanitize_or_inline __always_inline
#endif
/* Do not trap wrapping arithmetic within an annotated function. */
#ifdef CONFIG_UBSAN_SIGNED_WRAP
# define __signed_wrap __attribute__((no_sanitize("signed-integer-overflow")))
#else
# define __signed_wrap
#endif
/* Section for code which can't be instrumented at all */
#define __noinstr_section(section) \
noinline notrace __attribute((__section__(section))) \
__no_kcsan __no_sanitize_address __no_profile __no_sanitize_coverage \
__no_sanitize_memory
__no_sanitize_memory __signed_wrap
#define noinstr __noinstr_section(".noinstr.text")
......
This diff is collapsed.
......@@ -33,20 +33,14 @@
#include <linux/sprintf.h>
#include <linux/static_call_types.h>
#include <linux/instruction_pointer.h>
#include <linux/wordpart.h>
#include <asm/byteorder.h>
#include <uapi/linux/kernel.h>
#define STACK_MAGIC 0xdeadbeef
/**
* REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
* @x: value to repeat
*
* NOTE: @x is not checked for > 0xff; larger values produce odd results.
*/
#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
/* generic data direction definitions */
#define READ 0
#define WRITE 1
......@@ -60,34 +54,6 @@
} \
)
/**
* upper_32_bits - return bits 32-63 of a number
* @n: the number we're accessing
*
* A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
* the "right shift count >= width of type" warning when that quantity is
* 32-bits.
*/
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
/**
* lower_32_bits - return bits 0-31 of a number
* @n: the number we're accessing
*/
#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
/**
* upper_16_bits - return bits 16-31 of a number
* @n: the number we're accessing
*/
#define upper_16_bits(n) ((u16)((n) >> 16))
/**
* lower_16_bits - return bits 0-15 of a number
* @n: the number we're accessing
*/
#define lower_16_bits(n) ((u16)((n) & 0xffff))
struct completion;
struct user;
......@@ -199,12 +165,6 @@ static inline void might_fault(void) { }
void do_exit(long error_code) __noreturn;
extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints);
extern unsigned long long memparse(const char *ptr, char **retptr);
extern bool parse_option_str(const char *str, const char *option);
extern char *next_arg(char *args, char **param, char **val);
extern int core_kernel_text(unsigned long addr);
extern int __kernel_text_address(unsigned long addr);
extern int kernel_text_address(unsigned long addr);
......
......@@ -31,8 +31,10 @@
* credit to Christian Biere.
*/
#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
#define type_min(T) ((T)((T)-type_max(T)-(T)1))
#define __type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
#define type_max(t) __type_max(typeof(t))
#define __type_min(T) ((T)((T)-type_max(T)-(T)1))
#define type_min(t) __type_min(typeof(t))
/*
* Avoids triggering -Wtype-limits compilation warning,
......@@ -57,45 +59,122 @@ static inline bool __must_check __must_check_overflow(bool overflow)
* @b: second addend
* @d: pointer to store sum
*
* Returns 0 on success.
* Returns true on wrap-around, false otherwise.
*
* *@d holds the results of the attempted addition, but is not considered
* "safe for use" on a non-zero return value, which indicates that the
* sum has overflowed or been truncated.
* *@d holds the results of the attempted addition, regardless of whether
* wrap-around occurred.
*/
#define check_add_overflow(a, b, d) \
__must_check_overflow(__builtin_add_overflow(a, b, d))
/**
* wrapping_add() - Intentionally perform a wrapping addition
* @type: type for result of calculation
* @a: first addend
* @b: second addend
*
* Return the potentially wrapped-around addition without
* tripping any wrap-around sanitizers that may be enabled.
*/
#define wrapping_add(type, a, b) \
({ \
type __val; \
__builtin_add_overflow(a, b, &__val); \
__val; \
})
/**
* wrapping_assign_add() - Intentionally perform a wrapping increment assignment
* @var: variable to be incremented
* @offset: amount to add
*
* Increments @var by @offset with wrap-around. Returns the resulting
* value of @var. Will not trip any wrap-around sanitizers.
*
* Returns the new value of @var.
*/
#define wrapping_assign_add(var, offset) \
({ \
typeof(var) *__ptr = &(var); \
*__ptr = wrapping_add(typeof(var), *__ptr, offset); \
})
/**
* check_sub_overflow() - Calculate subtraction with overflow checking
* @a: minuend; value to subtract from
* @b: subtrahend; value to subtract from @a
* @d: pointer to store difference
*
* Returns 0 on success.
* Returns true on wrap-around, false otherwise.
*
* *@d holds the results of the attempted subtraction, but is not considered
* "safe for use" on a non-zero return value, which indicates that the
* difference has underflowed or been truncated.
* *@d holds the results of the attempted subtraction, regardless of whether
* wrap-around occurred.
*/
#define check_sub_overflow(a, b, d) \
__must_check_overflow(__builtin_sub_overflow(a, b, d))
/**
* wrapping_sub() - Intentionally perform a wrapping subtraction
* @type: type for result of calculation
* @a: minuend; value to subtract from
* @b: subtrahend; value to subtract from @a
*
* Return the potentially wrapped-around subtraction without
* tripping any wrap-around sanitizers that may be enabled.
*/
#define wrapping_sub(type, a, b) \
({ \
type __val; \
__builtin_sub_overflow(a, b, &__val); \
__val; \
})
/**
* wrapping_assign_sub() - Intentionally perform a wrapping decrement assign
* @var: variable to be decremented
* @offset: amount to subtract
*
* Decrements @var by @offset with wrap-around. Returns the resulting
* value of @var. Will not trip any wrap-around sanitizers.
*
* Returns the new value of @var.
*/
#define wrapping_assign_sub(var, offset) \
({ \
typeof(var) *__ptr = &(var); \
*__ptr = wrapping_sub(typeof(var), *__ptr, offset); \
})
/**
* check_mul_overflow() - Calculate multiplication with overflow checking
* @a: first factor
* @b: second factor
* @d: pointer to store product
*
* Returns 0 on success.
* Returns true on wrap-around, false otherwise.
*
* *@d holds the results of the attempted multiplication, but is not
* considered "safe for use" on a non-zero return value, which indicates
* that the product has overflowed or been truncated.
* *@d holds the results of the attempted multiplication, regardless of whether
* wrap-around occurred.
*/
#define check_mul_overflow(a, b, d) \
__must_check_overflow(__builtin_mul_overflow(a, b, d))
/**
* wrapping_mul() - Intentionally perform a wrapping multiplication
* @type: type for result of calculation
* @a: first factor
* @b: second factor
*
* Return the potentially wrapped-around multiplication without
* tripping any wrap-around sanitizers that may be enabled.
*/
#define wrapping_mul(type, a, b) \
({ \
type __val; \
__builtin_mul_overflow(a, b, &__val); \
__val; \
})
/**
* check_shl_overflow() - Calculate a left-shifted value and check overflow
* @a: Value to be shifted
......@@ -120,7 +199,7 @@ static inline bool __must_check __must_check_overflow(bool overflow)
typeof(a) _a = a; \
typeof(s) _s = s; \
typeof(d) _d = d; \
u64 _a_full = _a; \
unsigned long long _a_full = _a; \
unsigned int _to_shift = \
is_non_negative(_s) && _s < 8 * sizeof(*d) ? _s : 0; \
*_d = (_a_full << _to_shift); \
......@@ -130,10 +209,10 @@ static inline bool __must_check __must_check_overflow(bool overflow)
#define __overflows_type_constexpr(x, T) ( \
is_unsigned_type(typeof(x)) ? \
(x) > type_max(typeof(T)) : \
(x) > type_max(T) : \
is_unsigned_type(typeof(T)) ? \
(x) < 0 || (x) > type_max(typeof(T)) : \
(x) < type_min(typeof(T)) || (x) > type_max(typeof(T)))
(x) < 0 || (x) > type_max(T) : \
(x) < type_min(T) || (x) > type_max(T))
#define __overflows_type(x, T) ({ \
typeof(T) v = 0; \
......
......@@ -136,7 +136,8 @@ static inline unsigned int refcount_read(const refcount_t *r)
return atomic_read(&r->refs);
}
static inline __must_check bool __refcount_add_not_zero(int i, refcount_t *r, int *oldp)
static inline __must_check __signed_wrap
bool __refcount_add_not_zero(int i, refcount_t *r, int *oldp)
{
int old = refcount_read(r);
......@@ -177,7 +178,8 @@ static inline __must_check bool refcount_add_not_zero(int i, refcount_t *r)
return __refcount_add_not_zero(i, r, NULL);
}
static inline void __refcount_add(int i, refcount_t *r, int *oldp)
static inline __signed_wrap
void __refcount_add(int i, refcount_t *r, int *oldp)
{
int old = atomic_fetch_add_relaxed(i, &r->refs);
......@@ -256,7 +258,8 @@ static inline void refcount_inc(refcount_t *r)
__refcount_inc(r, NULL);
}
static inline __must_check bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
static inline __must_check __signed_wrap
bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
{
int old = atomic_fetch_sub_release(i, &r->refs);
......
......@@ -2,6 +2,7 @@
#ifndef _LINUX_STRING_H_
#define _LINUX_STRING_H_
#include <linux/args.h>
#include <linux/array_size.h>
#include <linux/compiler.h> /* for inline */
#include <linux/types.h> /* for size_t */
......@@ -66,12 +67,79 @@ extern char * strcpy(char *,const char *);
#ifndef __HAVE_ARCH_STRNCPY
extern char * strncpy(char *,const char *, __kernel_size_t);
#endif
#ifndef __HAVE_ARCH_STRSCPY
ssize_t strscpy(char *, const char *, size_t);
#endif
ssize_t sized_strscpy(char *, const char *, size_t);
/*
* The 2 argument style can only be used when dst is an array with a
* known size.
*/
#define __strscpy0(dst, src, ...) \
sized_strscpy(dst, src, sizeof(dst) + __must_be_array(dst))
#define __strscpy1(dst, src, size) sized_strscpy(dst, src, size)
#define __strscpy_pad0(dst, src, ...) \
sized_strscpy_pad(dst, src, sizeof(dst) + __must_be_array(dst))
#define __strscpy_pad1(dst, src, size) sized_strscpy_pad(dst, src, size)
/**
* strscpy - Copy a C-string into a sized buffer
* @dst: Where to copy the string to
* @src: Where to copy the string from
* @...: Size of destination buffer (optional)
*
* Copy the source string @src, or as much of it as fits, into the
* destination @dst buffer. The behavior is undefined if the string
* buffers overlap. The destination @dst buffer is always NUL terminated,
* unless it's zero-sized.
*
* The size argument @... is only required when @dst is not an array, or
* when the copy needs to be smaller than sizeof(@dst).
*
* Preferred to strncpy() since it always returns a valid string, and
* doesn't unnecessarily force the tail of the destination buffer to be
* zero padded. If padding is desired please use strscpy_pad().
*
* Returns the number of characters copied in @dst (not including the
* trailing %NUL) or -E2BIG if @size is 0 or the copy from @src was
* truncated.
*/
#define strscpy(dst, src, ...) \
CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
#define sized_strscpy_pad(dest, src, count) ({ \
char *__dst = (dest); \
const char *__src = (src); \
const size_t __count = (count); \
ssize_t __wrote; \
\
__wrote = sized_strscpy(__dst, __src, __count); \
if (__wrote >= 0 && __wrote < __count) \
memset(__dst + __wrote + 1, 0, __count - __wrote - 1); \
__wrote; \
})
/* Wraps calls to strscpy()/memset(), no arch specific code required */
ssize_t strscpy_pad(char *dest, const char *src, size_t count);
/**
* strscpy_pad() - Copy a C-string into a sized buffer
* @dst: Where to copy the string to
* @src: Where to copy the string from
* @...: Size of destination buffer
*
* Copy the string, or as much of it as fits, into the dest buffer. The
* behavior is undefined if the string buffers overlap. The destination
* buffer is always %NUL terminated, unless it's zero-sized.
*
* If the source string is shorter than the destination buffer, the
* remaining bytes in the buffer will be filled with %NUL bytes.
*
* For full explanation of why you may want to consider using the
* 'strscpy' functions please see the function docstring for strscpy().
*
* Returns:
* * The number of characters copied (not including the trailing %NULs)
* * -E2BIG if count is 0 or @src was truncated.
*/
#define strscpy_pad(dst, src, ...) \
CONCATENATE(__strscpy_pad, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
#ifndef __HAVE_ARCH_STRCAT
extern char * strcat(char *, const char *);
......@@ -219,9 +287,17 @@ extern void *kvmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp);
/* lib/argv_split.c */
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
/* lib/cmdline.c */
extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints);
extern unsigned long long memparse(const char *ptr, char **retptr);
extern bool parse_option_str(const char *str, const char *option);
extern char *next_arg(char *args, char **param, char **val);
extern bool sysfs_streq(const char *s1, const char *s2);
int match_string(const char * const *array, size_t n, const char *string);
int __sysfs_match_string(const char * const *array, size_t n, const char *s);
......
......@@ -42,4 +42,15 @@ static inline const char *str_yes_no(bool v)
return v ? "yes" : "no";
}
/**
* str_plural - Return the simple pluralization based on English counts
* @num: Number used for deciding pluralization
*
* If @num is 1, returns empty string, otherwise returns "s".
*/
static inline const char *str_plural(size_t num)
{
return num == 1 ? "" : "s";
}
#endif
......@@ -17,14 +17,18 @@ static inline bool string_is_terminated(const char *s, int len)
return memchr(s, '\0', len) ? true : false;
}
/* Descriptions of the types of units to
* print in */
/* Descriptions of the types of units to print in */
enum string_size_units {
STRING_UNITS_10, /* use powers of 10^3 (standard SI) */
STRING_UNITS_2, /* use binary powers of 2^10 */
STRING_UNITS_MASK = BIT(0),
/* Modifiers */
STRING_UNITS_NO_SPACE = BIT(30),
STRING_UNITS_NO_BYTES = BIT(31),
};
int string_get_size(u64 size, u64 blk_size, enum string_size_units units,
int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
char *buf, int len);
int parse_int_array_user(const char __user *from, size_t count, int **array);
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_WORDPART_H
#define _LINUX_WORDPART_H
/**
* upper_32_bits - return bits 32-63 of a number
* @n: the number we're accessing
*
* A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
* the "right shift count >= width of type" warning when that quantity is
* 32-bits.
*/
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
/**
* lower_32_bits - return bits 0-31 of a number
* @n: the number we're accessing
*/
#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
/**
* upper_16_bits - return bits 16-31 of a number
* @n: the number we're accessing
*/
#define upper_16_bits(n) ((u16)((n) >> 16))
/**
* lower_16_bits - return bits 0-15 of a number
* @n: the number we're accessing
*/
#define lower_16_bits(n) ((u16)((n) & 0xffff))
/**
* REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
* @x: value to repeat
*
* NOTE: @x is not checked for > 0xff; larger values produce odd results.
*/
#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
#endif // _LINUX_WORDPART_H
......@@ -44,7 +44,9 @@ CONFIG_UBSAN_BOUNDS=y
# CONFIG_UBSAN_BOOL
# CONFIG_UBSAN_ENUM
# CONFIG_UBSAN_ALIGNMENT
CONFIG_UBSAN_SANITIZE_ALL=y
# Sampling-based heap out-of-bounds and use-after-free detection.
CONFIG_KFENCE=y
# Linked list integrity checking.
CONFIG_LIST_HARDENED=y
......@@ -93,6 +95,3 @@ CONFIG_SYN_COOKIES=y
# Attack surface reduction: Use the modern PTY interface (devpts) only.
# CONFIG_LEGACY_PTYS is not set
# Attack surface reduction: Use only modesetting video drivers.
# CONFIG_DRM_LEGACY is not set
......@@ -598,17 +598,6 @@ static int check_syslog_permissions(int type, int source)
if (syslog_action_restricted(type)) {
if (capable(CAP_SYSLOG))
goto ok;
/*
* For historical reasons, accept CAP_SYS_ADMIN too, with
* a warning.
*/
if (capable(CAP_SYS_ADMIN)) {
pr_warn_once("%s (%d): Attempt to access syslog with "
"CAP_SYS_ADMIN but no CAP_SYSLOG "
"(deprecated).\n",
current->comm, task_pid_nr(current));
goto ok;
}
return -EPERM;
}
ok:
......
......@@ -2353,11 +2353,15 @@ config ASYNC_RAID6_TEST
config TEST_HEXDUMP
tristate "Test functions located in the hexdump module at runtime"
config STRING_SELFTEST
tristate "Test string functions at runtime"
config STRING_KUNIT_TEST
tristate "KUnit test string functions at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config TEST_STRING_HELPERS
tristate "Test functions located in the string_helpers module at runtime"
config STRING_HELPERS_KUNIT_TEST
tristate "KUnit test string helpers at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config TEST_KSTRTOX
tristate "Test kstrto*() family of functions at runtime"
......@@ -2749,7 +2753,7 @@ config STACKINIT_KUNIT_TEST
config FORTIFY_KUNIT_TEST
tristate "Test fortified str*() and mem*() function internals at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT && FORTIFY_SOURCE
depends on KUNIT
default KUNIT_ALL_TESTS
help
Builds unit tests for checking internals of FORTIFY_SOURCE as used
......
# SPDX-License-Identifier: GPL-2.0-only
config ARCH_HAS_UBSAN_SANITIZE_ALL
config ARCH_HAS_UBSAN
bool
menuconfig UBSAN
......@@ -87,7 +87,6 @@ config UBSAN_LOCAL_BOUNDS
config UBSAN_SHIFT
bool "Perform checking for bit-shift overflows"
default UBSAN
depends on $(cc-option,-fsanitize=shift)
help
This option enables -fsanitize=shift which checks for bit-shift
......@@ -116,6 +115,20 @@ config UBSAN_UNREACHABLE
This option enables -fsanitize=unreachable which checks for control
flow reaching an expected-to-be-unreachable position.
config UBSAN_SIGNED_WRAP
bool "Perform checking for signed arithmetic wrap-around"
default UBSAN
depends on !COMPILE_TEST
depends on $(cc-option,-fsanitize=signed-integer-overflow)
help
This option enables -fsanitize=signed-integer-overflow which checks
for wrap-around of any arithmetic operations with signed integers.
This currently performs nearly no instrumentation due to the
kernel's use of -fno-strict-overflow which converts all would-be
arithmetic undefined behavior into wrap-around arithmetic. Future
sanitizer versions will allow for wrap-around checking (rather than
exclusively undefined behavior).
config UBSAN_BOOL
bool "Perform checking for non-boolean values used as boolean"
default UBSAN
......@@ -142,17 +155,6 @@ config UBSAN_ALIGNMENT
Enabling this option on architectures that support unaligned
accesses may produce a lot of false positives.
config UBSAN_SANITIZE_ALL
bool "Enable instrumentation for the entire kernel"
depends on ARCH_HAS_UBSAN_SANITIZE_ALL
default y
help
This option activates instrumentation for the entire kernel.
If you don't enable this option, you have to explicitly specify
UBSAN_SANITIZE := y for the files/directories you want to check for UB.
Enabling this option will get kernel image size increased
significantly.
config TEST_UBSAN
tristate "Module for testing for undefined behavior detection"
depends on m
......
......@@ -49,9 +49,9 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
percpu-refcount.o rhashtable.o base64.o \
once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \
generic-radix-tree.o bitmap-str.o
obj-$(CONFIG_STRING_SELFTEST) += test_string.o
obj-$(CONFIG_STRING_KUNIT_TEST) += string_kunit.o
obj-y += string_helpers.o
obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
obj-$(CONFIG_STRING_HELPERS_KUNIT_TEST) += string_helpers_kunit.o
obj-y += hexdump.o
obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
obj-y += kstrtox.o
......@@ -69,6 +69,7 @@ obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
obj-$(CONFIG_TEST_IDA) += test_ida.o
obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla)
CFLAGS_test_ubsan.o += $(call cc-disable-warning, unused-but-set-variable)
UBSAN_SANITIZE_test_ubsan.o := y
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
......@@ -399,6 +400,8 @@ obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o
CFLAGS_stackinit_kunit.o += $(call cc-disable-warning, switch-unreachable)
obj-$(CONFIG_STACKINIT_KUNIT_TEST) += stackinit_kunit.o
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced)
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread)
CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-truncation)
CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
obj-$(CONFIG_STRCAT_KUNIT_TEST) += strcat_kunit.o
......
This diff is collapsed.
......@@ -258,25 +258,84 @@ DEFINE_TEST_ARRAY(s64) = {
\
_of = check_ ## op ## _overflow(a, b, &_r); \
KUNIT_EXPECT_EQ_MSG(test, _of, of, \
"expected "fmt" "sym" "fmt" to%s overflow (type %s)\n", \
"expected check "fmt" "sym" "fmt" to%s overflow (type %s)\n", \
a, b, of ? "" : " not", #t); \
KUNIT_EXPECT_EQ_MSG(test, _r, r, \
"expected "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \
"expected check "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \
a, b, r, _r, #t); \
/* Check for internal macro side-effects. */ \
_of = check_ ## op ## _overflow(_a_orig++, _b_orig++, &_r); \
KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, "Unexpected " #op " macro side-effect!\n"); \
KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, "Unexpected " #op " macro side-effect!\n"); \
KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, \
"Unexpected check " #op " macro side-effect!\n"); \
KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \
"Unexpected check " #op " macro side-effect!\n"); \
\
_r = wrapping_ ## op(t, a, b); \
KUNIT_EXPECT_TRUE_MSG(test, _r == r, \
"expected wrap "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \
a, b, r, _r, #t); \
/* Check for internal macro side-effects. */ \
_a_orig = a; \
_b_orig = b; \
_r = wrapping_ ## op(t, _a_orig++, _b_orig++); \
KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, \
"Unexpected wrap " #op " macro side-effect!\n"); \
KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \
"Unexpected wrap " #op " macro side-effect!\n"); \
} while (0)
static int global_counter;
static void bump_counter(void)
{
global_counter++;
}
static int get_index(void)
{
volatile int index = 0;
bump_counter();
return index;
}
#define check_self_op(fmt, op, sym, a, b) do { \
typeof(a + 0) _a = a; \
typeof(b + 0) _b = b; \
typeof(a + 0) _a_sym = a; \
typeof(a + 0) _a_orig[1] = { a }; \
typeof(b + 0) _b_orig = b; \
typeof(b + 0) _b_bump = b + 1; \
typeof(a + 0) _r; \
\
_a_sym sym _b; \
_r = wrapping_ ## op(_a, _b); \
KUNIT_EXPECT_TRUE_MSG(test, _r == _a_sym, \
"expected "fmt" "#op" "fmt" == "fmt", got "fmt"\n", \
a, b, _a_sym, _r); \
KUNIT_EXPECT_TRUE_MSG(test, _a == _a_sym, \
"expected "fmt" "#op" "fmt" == "fmt", got "fmt"\n", \
a, b, _a_sym, _a); \
/* Check for internal macro side-effects. */ \
global_counter = 0; \
wrapping_ ## op(_a_orig[get_index()], _b_orig++); \
KUNIT_EXPECT_EQ_MSG(test, global_counter, 1, \
"Unexpected wrapping_" #op " macro side-effect on arg1!\n"); \
KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \
"Unexpected wrapping_" #op " macro side-effect on arg2!\n"); \
} while (0)
#define DEFINE_TEST_FUNC_TYPED(n, t, fmt) \
static void do_test_ ## n(struct kunit *test, const struct test_ ## n *p) \
{ \
/* check_{add,sub,mul}_overflow() and wrapping_{add,sub,mul} */ \
check_one_op(t, fmt, add, "+", p->a, p->b, p->sum, p->s_of); \
check_one_op(t, fmt, add, "+", p->b, p->a, p->sum, p->s_of); \
check_one_op(t, fmt, sub, "-", p->a, p->b, p->diff, p->d_of); \
check_one_op(t, fmt, mul, "*", p->a, p->b, p->prod, p->p_of); \
check_one_op(t, fmt, mul, "*", p->b, p->a, p->prod, p->p_of); \
/* wrapping_assign_{add,sub}() */ \
check_self_op(fmt, assign_add, +=, p->a, p->b); \
check_self_op(fmt, assign_add, +=, p->b, p->a); \
check_self_op(fmt, assign_sub, -=, p->a, p->b); \
} \
\
static void n ## _overflow_test(struct kunit *test) { \
......
......@@ -63,7 +63,16 @@ static bool stackinit_range_contains(char *haystack_start, size_t haystack_size,
#define FETCH_ARG_STRING(var) var
#define FETCH_ARG_STRUCT(var) &var
/*
* On m68k, if the leaf function test variable is longer than 8 bytes,
* the start of the stack frame moves. 8 is sufficiently large to
* test m68k char arrays, but leave it at 16 for other architectures.
*/
#ifdef CONFIG_M68K
#define FILL_SIZE_STRING 8
#else
#define FILL_SIZE_STRING 16
#endif
#define INIT_CLONE_SCALAR /**/
#define INIT_CLONE_STRING [FILL_SIZE_STRING]
......@@ -165,19 +174,23 @@ static noinline void test_ ## name (struct kunit *test) \
/* Verify all bytes overwritten with 0xFF. */ \
for (sum = 0, i = 0; i < target_size; i++) \
sum += (check_buf[i] != 0xFF); \
KUNIT_ASSERT_EQ_MSG(test, sum, 0, \
"leaf fill was not 0xFF!?\n"); \
/* Clear entire check buffer for later bit tests. */ \
memset(check_buf, 0x00, sizeof(check_buf)); \
/* Extract stack-defined variable contents. */ \
ignored = leaf_ ##name((unsigned long)&ignored, 0, \
FETCH_ARG_ ## which(zero)); \
/* \
* Delay the sum test to here to do as little as \
* possible between the two leaf function calls. \
*/ \
KUNIT_ASSERT_EQ_MSG(test, sum, 0, \
"leaf fill was not 0xFF!?\n"); \
\
/* Validate that compiler lined up fill and target. */ \
KUNIT_ASSERT_TRUE_MSG(test, \
stackinit_range_contains(fill_start, fill_size, \
target_start, target_size), \
"stack fill missed target!? " \
"stackframe was not the same between calls!? " \
"(fill %zu wide, target offset by %d)\n", \
fill_size, \
(int)((ssize_t)(uintptr_t)fill_start - \
......
......@@ -15,19 +15,20 @@
*/
#define __NO_FORTIFY
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/limits.h>
#include <linux/linkage.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/page.h>
#include <asm/rwonce.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <asm/word-at-a-time.h>
#include <asm/page.h>
#ifndef __HAVE_ARCH_STRNCASECMP
/**
......@@ -103,8 +104,7 @@ char *strncpy(char *dest, const char *src, size_t count)
EXPORT_SYMBOL(strncpy);
#endif
#ifndef __HAVE_ARCH_STRSCPY
ssize_t strscpy(char *dest, const char *src, size_t count)
ssize_t sized_strscpy(char *dest, const char *src, size_t count)
{
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
size_t max = count;
......@@ -170,8 +170,7 @@ ssize_t strscpy(char *dest, const char *src, size_t count)
return -E2BIG;
}
EXPORT_SYMBOL(strscpy);
#endif
EXPORT_SYMBOL(sized_strscpy);
/**
* stpcpy - copy a string from src to dest returning a pointer to the new end
......
......@@ -18,12 +18,14 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
/**
* string_get_size - get the size in the specified units
* @size: The size to be converted in blocks
* @blk_size: Size of the block (use 1 for size in bytes)
* @units: units to use (powers of 1000 or 1024)
* @units: Units to use (powers of 1000 or 1024), whether to include space separator
* @buf: buffer to format to
* @len: length of buffer
*
......@@ -37,11 +39,12 @@
int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
char *buf, int len)
{
enum string_size_units units_base = units & STRING_UNITS_MASK;
static const char *const units_10[] = {
"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
"", "k", "M", "G", "T", "P", "E", "Z", "Y",
};
static const char *const units_2[] = {
"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi",
};
static const char *const *const units_str[] = {
[STRING_UNITS_10] = units_10,
......@@ -66,7 +69,7 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
/* This is Napier's algorithm. Reduce the original block size to
*
* coefficient * divisor[units]^i
* coefficient * divisor[units_base]^i
*
* we do the reduction so both coefficients are just under 32 bits so
* that multiplying them together won't overflow 64 bits and we keep
......@@ -76,12 +79,12 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
* precision is in the coefficients.
*/
while (blk_size >> 32) {
do_div(blk_size, divisor[units]);
do_div(blk_size, divisor[units_base]);
i++;
}
while (size >> 32) {
do_div(size, divisor[units]);
do_div(size, divisor[units_base]);
i++;
}
......@@ -90,8 +93,8 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
size *= blk_size;
/* and logarithmically reduce it until it's just under the divisor */
while (size >= divisor[units]) {
remainder = do_div(size, divisor[units]);
while (size >= divisor[units_base]) {
remainder = do_div(size, divisor[units_base]);
i++;
}
......@@ -101,10 +104,10 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
for (j = 0; sf_cap*10 < 1000; j++)
sf_cap *= 10;
if (units == STRING_UNITS_2) {
if (units_base == STRING_UNITS_2) {
/* express the remainder as a decimal. It's currently the
* numerator of a fraction whose denominator is
* divisor[units], which is 1 << 10 for STRING_UNITS_2 */
* divisor[units_base], which is 1 << 10 for STRING_UNITS_2 */
remainder *= 1000;
remainder >>= 10;
}
......@@ -126,10 +129,12 @@ int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
if (i >= ARRAY_SIZE(units_2))
unit = "UNK";
else
unit = units_str[units][i];
unit = units_str[units_base][i];
return snprintf(buf, len, "%u%s %s", (u32)size,
tmp, unit);
return snprintf(buf, len, "%u%s%s%s%s", (u32)size, tmp,
(units & STRING_UNITS_NO_SPACE) ? "" : " ",
unit,
(units & STRING_UNITS_NO_BYTES) ? "" : "B");
}
EXPORT_SYMBOL(string_get_size);
......@@ -825,40 +830,6 @@ char **devm_kasprintf_strarray(struct device *dev, const char *prefix, size_t n)
}
EXPORT_SYMBOL_GPL(devm_kasprintf_strarray);
/**
* strscpy_pad() - Copy a C-string into a sized buffer
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @count: Size of destination buffer
*
* Copy the string, or as much of it as fits, into the dest buffer. The
* behavior is undefined if the string buffers overlap. The destination
* buffer is always %NUL terminated, unless it's zero-sized.
*
* If the source string is shorter than the destination buffer, zeros
* the tail of the destination buffer.
*
* For full explanation of why you may want to consider using the
* 'strscpy' functions please see the function docstring for strscpy().
*
* Returns:
* * The number of characters copied (not including the trailing %NUL)
* * -E2BIG if count is 0 or @src was truncated.
*/
ssize_t strscpy_pad(char *dest, const char *src, size_t count)
{
ssize_t written;
written = strscpy(dest, src, count);
if (written < 0 || written == count - 1)
return written;
memset(dest + written + 1, 0, count - written - 1);
return written;
}
EXPORT_SYMBOL(strscpy_pad);
/**
* skip_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
......@@ -1042,10 +1013,28 @@ EXPORT_SYMBOL(__read_overflow2_field);
void __write_overflow_field(size_t avail, size_t wanted) { }
EXPORT_SYMBOL(__write_overflow_field);
void fortify_panic(const char *name)
static const char * const fortify_func_name[] = {
#define MAKE_FORTIFY_FUNC_NAME(func) [MAKE_FORTIFY_FUNC(func)] = #func
EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC_NAME)
#undef MAKE_FORTIFY_FUNC_NAME
};
void __fortify_report(const u8 reason, const size_t avail, const size_t size)
{
const u8 func = FORTIFY_REASON_FUNC(reason);
const bool write = FORTIFY_REASON_DIR(reason);
const char *name;
name = fortify_func_name[umin(func, FORTIFY_FUNC_UNKNOWN)];
WARN(1, "%s: detected buffer overflow: %zu byte %s of buffer size %zu\n",
name, size, str_read_write(!write), avail);
}
EXPORT_SYMBOL(__fortify_report);
void __fortify_panic(const u8 reason, const size_t avail, const size_t size)
{
pr_emerg("detected buffer overflow in %s\n", name);
__fortify_report(reason, avail, size);
BUG();
}
EXPORT_SYMBOL(fortify_panic);
EXPORT_SYMBOL(__fortify_panic);
#endif /* CONFIG_FORTIFY_SOURCE */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Test cases for string functions.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <kunit/test.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
static __init int memset16_selftest(void)
static void test_memset16(struct kunit *test)
{
unsigned i, j, k;
u16 v, *p;
p = kmalloc(256 * 2 * 2, GFP_KERNEL);
if (!p)
return -1;
p = kunit_kzalloc(test, 256 * 2 * 2, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p);
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
......@@ -20,34 +26,27 @@ static __init int memset16_selftest(void)
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
if (v != 0xb1b2)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2,
"i:%d j:%d k:%d", i, j, k);
} else {
if (v != 0xa1a1)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int memset32_selftest(void)
static void test_memset32(struct kunit *test)
{
unsigned i, j, k;
u32 v, *p;
p = kmalloc(256 * 2 * 4, GFP_KERNEL);
if (!p)
return -1;
p = kunit_kzalloc(test, 256 * 2 * 4, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p);
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
......@@ -56,34 +55,27 @@ static __init int memset32_selftest(void)
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1a1a1)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
if (v != 0xb1b2b3b4)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2b3b4,
"i:%d j:%d k:%d", i, j, k);
} else {
if (v != 0xa1a1a1a1)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int memset64_selftest(void)
static void test_memset64(struct kunit *test)
{
unsigned i, j, k;
u64 v, *p;
p = kmalloc(256 * 2 * 8, GFP_KERNEL);
if (!p)
return -1;
p = kunit_kzalloc(test, 256 * 2 * 8, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p);
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
......@@ -92,27 +84,21 @@ static __init int memset64_selftest(void)
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1a1a1a1a1a1a1ULL)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1a1a1a1a1ULL,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
if (v != 0xb1b2b3b4b5b6b7b8ULL)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2b3b4b5b6b7b8ULL,
"i:%d j:%d k:%d", i, j, k);
} else {
if (v != 0xa1a1a1a1a1a1a1a1ULL)
goto fail;
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1a1a1a1a1ULL,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int strchr_selftest(void)
static void test_strchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
......@@ -121,26 +107,21 @@ static __init int strchr_selftest(void)
for (i = 0; i < strlen(test_string) + 1; i++) {
result = strchr(test_string, test_string[i]);
if (result - test_string != i)
return i + 'a';
KUNIT_ASSERT_EQ_MSG(test, result - test_string, i,
"char:%c", 'a' + i);
}
result = strchr(empty_string, '\0');
if (result != empty_string)
return 0x101;
KUNIT_ASSERT_PTR_EQ(test, result, empty_string);
result = strchr(empty_string, 'a');
if (result)
return 0x102;
KUNIT_ASSERT_NULL(test, result);
result = strchr(test_string, 'z');
if (result)
return 0x103;
return 0;
KUNIT_ASSERT_NULL(test, result);
}
static __init int strnchr_selftest(void)
static void test_strnchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
......@@ -151,35 +132,29 @@ static __init int strnchr_selftest(void)
for (j = 0; j < strlen(test_string) + 2; j++) {
result = strnchr(test_string, j, test_string[i]);
if (j <= i) {
if (!result)
continue;
return ((i + 'a') << 8) | j;
KUNIT_ASSERT_NULL_MSG(test, result,
"char:%c i:%d j:%d", 'a' + i, i, j);
} else {
KUNIT_ASSERT_EQ_MSG(test, result - test_string, i,
"char:%c i:%d j:%d", 'a' + i, i, j);
}
if (result - test_string != i)
return ((i + 'a') << 8) | j;
}
}
result = strnchr(empty_string, 0, '\0');
if (result)
return 0x10001;
KUNIT_ASSERT_NULL(test, result);
result = strnchr(empty_string, 1, '\0');
if (result != empty_string)
return 0x10002;
KUNIT_ASSERT_PTR_EQ(test, result, empty_string);
result = strnchr(empty_string, 1, 'a');
if (result)
return 0x10003;
KUNIT_ASSERT_NULL(test, result);
result = strnchr(NULL, 0, '\0');
if (result)
return 0x10004;
return 0;
KUNIT_ASSERT_NULL(test, result);
}
static __init int strspn_selftest(void)
static void test_strspn(struct kunit *test)
{
static const struct strspn_test {
const char str[16];
......@@ -187,71 +162,38 @@ static __init int strspn_selftest(void)
const char reject[16];
unsigned a;
unsigned r;
} tests[] __initconst = {
} tests[] = {
{ "foobar", "", "", 0, 6 },
{ "abba", "abc", "ABBA", 4, 4 },
{ "abba", "a", "b", 1, 1 },
{ "", "abc", "abc", 0, 0},
};
const struct strspn_test *s = tests;
size_t i, res;
size_t i;
for (i = 0; i < ARRAY_SIZE(tests); ++i, ++s) {
res = strspn(s->str, s->accept);
if (res != s->a)
return 0x100 + 2*i;
res = strcspn(s->str, s->reject);
if (res != s->r)
return 0x100 + 2*i + 1;
KUNIT_ASSERT_EQ_MSG(test, s->a, strspn(s->str, s->accept),
"i:%zu", i);
KUNIT_ASSERT_EQ_MSG(test, s->r, strcspn(s->str, s->reject),
"i:%zu", i);
}
return 0;
}
static __exit void string_selftest_remove(void)
{
}
static struct kunit_case string_test_cases[] = {
KUNIT_CASE(test_memset16),
KUNIT_CASE(test_memset32),
KUNIT_CASE(test_memset64),
KUNIT_CASE(test_strchr),
KUNIT_CASE(test_strnchr),
KUNIT_CASE(test_strspn),
{}
};
static __init int string_selftest_init(void)
{
int test, subtest;
test = 1;
subtest = memset16_selftest();
if (subtest)
goto fail;
test = 2;
subtest = memset32_selftest();
if (subtest)
goto fail;
test = 3;
subtest = memset64_selftest();
if (subtest)
goto fail;
static struct kunit_suite string_test_suite = {
.name = "string",
.test_cases = string_test_cases,
};
test = 4;
subtest = strchr_selftest();
if (subtest)
goto fail;
test = 5;
subtest = strnchr_selftest();
if (subtest)
goto fail;
test = 6;
subtest = strspn_selftest();
if (subtest)
goto fail;
pr_info("String selftests succeeded\n");
return 0;
fail:
pr_crit("String selftest failure %d.%08x\n", test, subtest);
return 0;
}
kunit_test_suites(&string_test_suite);
module_init(string_selftest_init);
module_exit(string_selftest_remove);
MODULE_LICENSE("GPL v2");
......@@ -11,6 +11,39 @@ typedef void(*test_ubsan_fp)(void);
#config, IS_ENABLED(config) ? "y" : "n"); \
} while (0)
static void test_ubsan_add_overflow(void)
{
volatile int val = INT_MAX;
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
val += 2;
}
static void test_ubsan_sub_overflow(void)
{
volatile int val = INT_MIN;
volatile int val2 = 2;
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
val -= val2;
}
static void test_ubsan_mul_overflow(void)
{
volatile int val = INT_MAX / 2;
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
val *= 3;
}
static void test_ubsan_negate_overflow(void)
{
volatile int val = INT_MIN;
UBSAN_TEST(CONFIG_UBSAN_SIGNED_WRAP);
val = -val;
}
static void test_ubsan_divrem_overflow(void)
{
volatile int val = 16;
......@@ -23,8 +56,8 @@ static void test_ubsan_divrem_overflow(void)
static void test_ubsan_shift_out_of_bounds(void)
{
volatile int neg = -1, wrap = 4;
int val1 = 10;
int val2 = INT_MAX;
volatile int val1 = 10;
volatile int val2 = INT_MAX;
UBSAN_TEST(CONFIG_UBSAN_SHIFT, "negative exponent");
val1 <<= neg;
......@@ -90,6 +123,10 @@ static void test_ubsan_misaligned_access(void)
}
static const test_ubsan_fp test_ubsan_array[] = {
test_ubsan_add_overflow,
test_ubsan_sub_overflow,
test_ubsan_mul_overflow,
test_ubsan_negate_overflow,
test_ubsan_shift_out_of_bounds,
test_ubsan_out_of_bounds,
test_ubsan_load_invalid_value,
......
......@@ -222,6 +222,74 @@ static void ubsan_epilogue(void)
check_panic_on_warn("UBSAN");
}
static void handle_overflow(struct overflow_data *data, void *lhs,
void *rhs, char op)
{
struct type_descriptor *type = data->type;
char lhs_val_str[VALUE_LENGTH];
char rhs_val_str[VALUE_LENGTH];
if (suppress_report(&data->location))
return;
ubsan_prologue(&data->location, type_is_signed(type) ?
"signed-integer-overflow" :
"unsigned-integer-overflow");
val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs);
val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs);
pr_err("%s %c %s cannot be represented in type %s\n",
lhs_val_str,
op,
rhs_val_str,
type->type_name);
ubsan_epilogue();
}
void __ubsan_handle_add_overflow(void *data,
void *lhs, void *rhs)
{
handle_overflow(data, lhs, rhs, '+');
}
EXPORT_SYMBOL(__ubsan_handle_add_overflow);
void __ubsan_handle_sub_overflow(void *data,
void *lhs, void *rhs)
{
handle_overflow(data, lhs, rhs, '-');
}
EXPORT_SYMBOL(__ubsan_handle_sub_overflow);
void __ubsan_handle_mul_overflow(void *data,
void *lhs, void *rhs)
{
handle_overflow(data, lhs, rhs, '*');
}
EXPORT_SYMBOL(__ubsan_handle_mul_overflow);
void __ubsan_handle_negate_overflow(void *_data, void *old_val)
{
struct overflow_data *data = _data;
char old_val_str[VALUE_LENGTH];
if (suppress_report(&data->location))
return;
ubsan_prologue(&data->location, "negation-overflow");
val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val);
pr_err("negation of %s cannot be represented in type %s:\n",
old_val_str, data->type->type_name);
ubsan_epilogue();
}
EXPORT_SYMBOL(__ubsan_handle_negate_overflow);
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs)
{
struct overflow_data *data = _data;
......
......@@ -124,6 +124,10 @@ typedef s64 s_max;
typedef u64 u_max;
#endif
void __ubsan_handle_add_overflow(void *data, void *lhs, void *rhs);
void __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs);
void __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);
void __ubsan_handle_negate_overflow(void *_data, void *old_val);
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs);
void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void *ptr);
void __ubsan_handle_type_mismatch_v1(void *_data, void *ptr);
......
......@@ -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)
......
......@@ -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)
// 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)
// 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)
......@@ -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");
}
}
......
......@@ -3620,6 +3620,18 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
}
if (!save_insn->visited) {
/*
* If the restore hint insn is at the
* beginning of a basic block and was
* branched to from elsewhere, and the
* save insn hasn't been visited yet,
* defer following this branch for now.
* It will be seen later via the
* straight-line path.
*/
if (!prev_insn)
return 0;
WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
return 1;
}
......
......@@ -6,6 +6,7 @@
*
* Yes, this is unfortunate. A better solution is in the works.
*/
NORETURN(__fortify_panic)
NORETURN(__kunit_abort)
NORETURN(__module_put_and_kthread_exit)
NORETURN(__reiserfs_panic)
......@@ -22,7 +23,6 @@ NORETURN(do_exit)
NORETURN(do_group_exit)
NORETURN(do_task_dead)
NORETURN(ex_handler_msr_mce)
NORETURN(fortify_panic)
NORETURN(hlt_play_dead)
NORETURN(hv_ghcb_terminate)
NORETURN(kthread_complete_and_exit)
......
../../../../../../include/linux/wordpart.h
\ No newline at end of file
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