hardening updates for v6.9-rc1

- 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
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmXvm5kWHGtlZXNjb29r
 QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJiQqD/4mM6SWZpYHKlR1nEiqIyz7Hqr9
 g4oguuw6HIVNJXLyeBI5Hd43CTeHPA0e++EETqhUAt7HhErxfYJY+JB221nRYmu+
 zhhQ7N/xbTMV/Je7AR03kQjhiMm8LyEcM2X4BNrsAcoCieQzmO3g0zSp8ISzLUE0
 PEEmf1lOzMe3gK2KOFCPt5Hiz9sGWyN6at+BQubY18tQGtjEXYAQNXkpD5qhGn4a
 EF693r/17wmc8hvSsjf4AGaWy1k8crG0WfpMCZsaqftjj0BbvOC60IDyx4eFjpcy
 tGyAJKETq161AkCdNweIh2Q107fG3tm0fcvw2dv8Wt1eQCko6M8dUGCBinQs/thh
 TexjJFS/XbSz+IvxLqgU+C5qkOP23E0M9m1dbIbOFxJAya/5n16WOBlGr3ae2Wdq
 /+t8wVSJw3vZiku5emWdFYP1VsdIHUjVa5QizFaaRhzLGRwhxVV49SP4IQC/5oM5
 3MAgNOFTP6yRQn9Y9wP+SZs+SsfaIE7yfKa9zOi4S+Ve+LI2v4YFhh8NCRiLkeWZ
 R1dhp8Pgtuq76f/v0qUaWcuuVeGfJ37M31KOGIhi1sI/3sr7UMrngL8D1+F8UZMi
 zcLu+x4GtfUZCHl6znx1rNUBqE5S/5ndVhLpOqfCXKaQ+RAm7lkOJ3jXE2VhNkhp
 yVEmeSOLnlCaQjZvXQ==
 =OP+o
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2024-03-12 14:49:30 -07:00
commit 216532e147
71 changed files with 1952 additions and 691 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");
}

View File

@ -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));

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -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 = {

View File

@ -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,

View File

@ -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;

View File

@ -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")

View File

@ -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;

View File

@ -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",

View File

@ -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) {

View File

@ -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

View File

@ -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");
}

View File

@ -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

View File

@ -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>

View File

@ -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++) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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)))

View File

@ -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")

View File

@ -2,6 +2,7 @@
#ifndef _LINUX_FORTIFY_STRING_H_
#define _LINUX_FORTIFY_STRING_H_
#include <linux/bitfield.h>
#include <linux/bug.h>
#include <linux/const.h>
#include <linux/limits.h>
@ -9,7 +10,46 @@
#define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable
#define __RENAME(x) __asm__(#x)
void fortify_panic(const char *name) __noreturn __cold;
#define FORTIFY_REASON_DIR(r) FIELD_GET(BIT(0), r)
#define FORTIFY_REASON_FUNC(r) FIELD_GET(GENMASK(7, 1), r)
#define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \
FIELD_PREP(GENMASK(7, 1), func))
#ifndef fortify_panic
# define fortify_panic(func, write, avail, size, retfail) \
__fortify_panic(FORTIFY_REASON(func, write), avail, size)
#endif
#define FORTIFY_READ 0
#define FORTIFY_WRITE 1
#define EACH_FORTIFY_FUNC(macro) \
macro(strncpy), \
macro(strnlen), \
macro(strlen), \
macro(strscpy), \
macro(strlcat), \
macro(strcat), \
macro(strncat), \
macro(memset), \
macro(memcpy), \
macro(memmove), \
macro(memscan), \
macro(memcmp), \
macro(memchr), \
macro(memchr_inv), \
macro(kmemdup), \
macro(strcpy), \
macro(UNKNOWN),
#define MAKE_FORTIFY_FUNC(func) FORTIFY_FUNC_##func
enum fortify_func {
EACH_FORTIFY_FUNC(MAKE_FORTIFY_FUNC)
};
void __fortify_report(const u8 reason, const size_t avail, const size_t size);
void __fortify_panic(const u8 reason, const size_t avail, const size_t size) __cold __noreturn;
void __read_overflow(void) __compiletime_error("detected read beyond size of object (1st parameter)");
void __read_overflow2(void) __compiletime_error("detected read beyond size of object (2nd parameter)");
void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("detected read beyond size of field (2nd parameter); maybe use struct_group()?");
@ -143,7 +183,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
if (__compiletime_lessthan(p_size, size))
__write_overflow();
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p);
return __underlying_strncpy(p, q, size);
}
@ -174,7 +214,7 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
/* Do not check characters beyond the end of p. */
ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
if (p_size <= ret && maxlen != ret)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, p_size, ret + 1, ret);
return ret;
}
@ -210,31 +250,13 @@ __kernel_size_t __fortify_strlen(const char * const POS p)
return __underlying_strlen(p);
ret = strnlen(p, p_size);
if (p_size <= ret)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret);
return ret;
}
/* Defined after fortified strnlen() to reuse it. */
extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(strscpy);
/**
* strscpy - Copy a C-string into a sized buffer
*
* @p: Where to copy the string to
* @q: Where to copy the string from
* @size: Size of destination buffer
*
* Copy the source string @q, or as much of it as fits, into the destination
* @p buffer. The behavior is undefined if the string buffers overlap. The
* destination @p buffer is always NUL terminated, unless it's zero-sized.
*
* 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 @p (not including the
* trailing %NUL) or -E2BIG if @size is 0 or the copy of @q was truncated.
*/
__FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, size_t size)
extern ssize_t __real_strscpy(char *, const char *, size_t) __RENAME(sized_strscpy);
__FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const POS q, size_t size)
{
/* Use string size rather than possible enclosing struct size. */
const size_t p_size = __member_size(p);
@ -278,8 +300,8 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s
* Generate a runtime write overflow error if len is greater than
* p_size.
*/
if (len > p_size)
fortify_panic(__func__);
if (p_size < len)
fortify_panic(FORTIFY_FUNC_strscpy, FORTIFY_WRITE, p_size, len, -E2BIG);
/*
* We can now safely call vanilla strscpy because we are protected from:
@ -337,7 +359,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)
/* Give up if string is already overflowed. */
if (p_size <= p_len)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_READ, p_size, p_len + 1, wanted);
if (actual >= avail) {
copy_len = avail - p_len - 1;
@ -346,7 +368,7 @@ size_t strlcat(char * const POS p, const char * const POS q, size_t avail)
/* Give up if copy will overflow. */
if (p_size <= actual)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strlcat, FORTIFY_WRITE, p_size, actual + 1, wanted);
__underlying_memcpy(p + p_len, q, copy_len);
p[actual] = '\0';
@ -373,9 +395,10 @@ __FORTIFY_INLINE __diagnose_as(__builtin_strcat, 1, 2)
char *strcat(char * const POS p, const char *q)
{
const size_t p_size = __member_size(p);
const size_t wanted = strlcat(p, q, p_size);
if (strlcat(p, q, p_size) >= p_size)
fortify_panic(__func__);
if (p_size <= wanted)
fortify_panic(FORTIFY_FUNC_strcat, FORTIFY_WRITE, p_size, wanted + 1, p);
return p;
}
@ -404,20 +427,21 @@ char *strncat(char * const POS p, const char * const POS q, __kernel_size_t coun
{
const size_t p_size = __member_size(p);
const size_t q_size = __member_size(q);
size_t p_len, copy_len;
size_t p_len, copy_len, total;
if (p_size == SIZE_MAX && q_size == SIZE_MAX)
return __underlying_strncat(p, q, count);
p_len = strlen(p);
copy_len = strnlen(q, count);
if (p_size < p_len + copy_len + 1)
fortify_panic(__func__);
total = p_len + copy_len + 1;
if (p_size < total)
fortify_panic(FORTIFY_FUNC_strncat, FORTIFY_WRITE, p_size, total, p);
__underlying_memcpy(p + p_len, q, copy_len);
p[p_len + copy_len] = '\0';
return p;
}
__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
__FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size,
const size_t p_size,
const size_t p_size_field)
{
@ -452,7 +476,8 @@ __FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
* lengths are unknown.)
*/
if (p_size != SIZE_MAX && p_size < size)
fortify_panic("memset");
fortify_panic(FORTIFY_FUNC_memset, FORTIFY_WRITE, p_size, size, true);
return false;
}
#define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({ \
@ -506,7 +531,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
const size_t q_size,
const size_t p_size_field,
const size_t q_size_field,
const char *func)
const u8 func)
{
if (__builtin_constant_p(size)) {
/*
@ -550,9 +575,10 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
* (The SIZE_MAX test is to optimize away checks where the buffer
* lengths are unknown.)
*/
if ((p_size != SIZE_MAX && p_size < size) ||
(q_size != SIZE_MAX && q_size < size))
fortify_panic(func);
if (p_size != SIZE_MAX && p_size < size)
fortify_panic(func, FORTIFY_WRITE, p_size, size, true);
else if (q_size != SIZE_MAX && q_size < size)
fortify_panic(func, FORTIFY_READ, p_size, size, true);
/*
* Warn when writing beyond destination field size.
@ -585,7 +611,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
const size_t __q_size_field = (q_size_field); \
WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size, \
__q_size, __p_size_field, \
__q_size_field, #op), \
__q_size_field, FORTIFY_FUNC_ ##op), \
#op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \
__fortify_size, \
"field \"" #p "\" at " FILE_LINE, \
@ -652,7 +678,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)
if (__compiletime_lessthan(p_size, size))
__read_overflow();
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, p_size, size, NULL);
return __real_memscan(p, c, size);
}
@ -668,8 +694,10 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t
if (__compiletime_lessthan(q_size, size))
__read_overflow2();
}
if (p_size < size || q_size < size)
fortify_panic(__func__);
if (p_size < size)
fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, p_size, size, INT_MIN);
else if (q_size < size)
fortify_panic(FORTIFY_FUNC_memcmp, FORTIFY_READ, q_size, size, INT_MIN);
return __underlying_memcmp(p, q, size);
}
@ -681,7 +709,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size)
if (__compiletime_lessthan(p_size, size))
__read_overflow();
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, p_size, size, NULL);
return __underlying_memchr(p, c, size);
}
@ -693,7 +721,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)
if (__compiletime_lessthan(p_size, size))
__read_overflow();
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, p_size, size, NULL);
return __real_memchr_inv(p, c, size);
}
@ -706,7 +734,7 @@ __FORTIFY_INLINE void *kmemdup(const void * const POS0 p, size_t size, gfp_t gfp
if (__compiletime_lessthan(p_size, size))
__read_overflow();
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size, NULL);
return __real_kmemdup(p, size, gfp);
}
@ -743,7 +771,7 @@ char *strcpy(char * const POS p, const char * const POS q)
__write_overflow();
/* Run-time check for dynamic size overflow. */
if (p_size < size)
fortify_panic(__func__);
fortify_panic(FORTIFY_FUNC_strcpy, FORTIFY_WRITE, p_size, size, p);
__underlying_memcpy(p, q, size);
return p;
}

View File

@ -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);

View File

@ -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; \

View File

@ -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);

View File

@ -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);
/* Wraps calls to strscpy()/memset(), no arch specific code required */
ssize_t strscpy_pad(char *dest, const char *src, size_t count);
/*
* 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; \
})
/**
* 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);

View File

@ -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

View File

@ -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);

42
include/linux/wordpart.h Normal file
View File

@ -0,0 +1,42 @@
/* 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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -1,5 +1,5 @@
# 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

View File

@ -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

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Runtime test cases for CONFIG_FORTIFY_SOURCE that aren't expected to
* Oops the kernel on success. (For those, see drivers/misc/lkdtm/fortify.c)
* Runtime test cases for CONFIG_FORTIFY_SOURCE. For testing memcpy(),
* see FORTIFY_MEM_* tests in LKDTM (drivers/misc/lkdtm/fortify.c).
*
* For corner cases with UBSAN, try testing with:
*
@ -15,17 +15,55 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* Redefine fortify_panic() to track failures. */
void fortify_add_kunit_error(int write);
#define fortify_panic(func, write, avail, size, retfail) do { \
__fortify_report(FORTIFY_REASON(func, write), avail, size); \
fortify_add_kunit_error(write); \
return (retfail); \
} while (0)
#include <kunit/device.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
/* Handle being built without CONFIG_FORTIFY_SOURCE */
#ifndef __compiletime_strlen
# define __compiletime_strlen __builtin_strlen
#endif
static struct kunit_resource read_resource;
static struct kunit_resource write_resource;
static int fortify_read_overflows;
static int fortify_write_overflows;
static const char array_of_10[] = "this is 10";
static const char *ptr_of_11 = "this is 11!";
static char array_unknown[] = "compiler thinks I might change";
void fortify_add_kunit_error(int write)
{
struct kunit_resource *resource;
struct kunit *current_test;
current_test = kunit_get_current_test();
if (!current_test)
return;
resource = kunit_find_named_resource(current_test,
write ? "fortify_write_overflows"
: "fortify_read_overflows");
if (!resource)
return;
(*(int *)resource->data)++;
kunit_put_resource(resource);
}
static void known_sizes_test(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, __compiletime_strlen("88888888"), 8);
@ -308,6 +346,610 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
} while (0)
DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc)
/*
* We can't have an array at the end of a structure or else
* builds without -fstrict-flex-arrays=3 will report them as
* being an unknown length. Additionally, add bytes before
* and after the string to catch over/underflows if tests
* fail.
*/
struct fortify_padding {
unsigned long bytes_before;
char buf[32];
unsigned long bytes_after;
};
/* Force compiler into not being able to resolve size at compile-time. */
static volatile int unconst;
static void strlen_test(struct kunit *test)
{
struct fortify_padding pad = { };
int i, end = sizeof(pad.buf) - 1;
/* Fill 31 bytes with valid characters. */
for (i = 0; i < sizeof(pad.buf) - 1; i++)
pad.buf[i] = i + '0';
/* Trailing bytes are still %NUL. */
KUNIT_EXPECT_EQ(test, pad.buf[end], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* String is terminated, so strlen() is valid. */
KUNIT_EXPECT_EQ(test, strlen(pad.buf), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Make string unterminated, and recount. */
pad.buf[end] = 'A';
end = sizeof(pad.buf);
KUNIT_EXPECT_EQ(test, strlen(pad.buf), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
}
static void strnlen_test(struct kunit *test)
{
struct fortify_padding pad = { };
int i, end = sizeof(pad.buf) - 1;
/* Fill 31 bytes with valid characters. */
for (i = 0; i < sizeof(pad.buf) - 1; i++)
pad.buf[i] = i + '0';
/* Trailing bytes are still %NUL. */
KUNIT_EXPECT_EQ(test, pad.buf[end], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* String is terminated, so strnlen() is valid. */
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, sizeof(pad.buf)), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* A truncated strnlen() will be safe, too. */
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, sizeof(pad.buf) / 2),
sizeof(pad.buf) / 2);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Make string unterminated, and recount. */
pad.buf[end] = 'A';
end = sizeof(pad.buf);
/* Reading beyond with strncpy() will fail. */
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
/* Early-truncated is safe still, though. */
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
end = sizeof(pad.buf) / 2;
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void strcpy_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf) + 1] = { };
int i;
/* Fill 31 bytes with valid characters. */
for (i = 0; i < sizeof(src) - 2; i++)
src[i] = i + '0';
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strcpy() 1 less than of max size. */
KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Only last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
src[sizeof(src) - 2] = 'A';
/* But now we trip the overflow checking. */
KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
/* Trailing %NUL -- thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
src[sizeof(src) - 1] = 'A';
/* And for sure now, two bytes past. */
KUNIT_ASSERT_TRUE(test, strcpy(pad.buf, src)
== pad.buf);
/*
* Which trips both the strlen() on the unterminated src,
* and the resulting copy attempt.
*/
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
/* Trailing %NUL -- thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strncpy_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strncpy() 1 less than of max size. */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
sizeof(pad.buf) + unconst - 1)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Only last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* Legitimate (though unterminated) max-size strncpy. */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
sizeof(pad.buf) + unconst)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* No trailing %NUL -- thanks strncpy API. */
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* But we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Now verify that FORTIFY is working... */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
sizeof(pad.buf) + unconst + 1)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And further... */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
sizeof(pad.buf) + unconst + 2)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strscpy_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strscpy() 1 less than of max size. */
KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
sizeof(pad.buf) + unconst - 1),
-E2BIG);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Keeping space for %NUL, last two bytes should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* Legitimate max-size strscpy. */
KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
sizeof(pad.buf) + unconst),
-E2BIG);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* A trailing %NUL will exist. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* Now verify that FORTIFY is working... */
KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
sizeof(pad.buf) + unconst + 1),
-E2BIG);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And much further... */
KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
sizeof(src) * 2 + unconst),
-E2BIG);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strcat_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf) / 2] = { };
char one[] = "A";
char two[] = "BC";
int i;
/* Fill 15 bytes with valid characters. */
for (i = 0; i < sizeof(src) - 1; i++)
src[i] = i + 'A';
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strcat() using less than half max size. */
KUNIT_ASSERT_TRUE(test, strcat(pad.buf, src) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Legitimate strcat() now 2 bytes shy of end. */
KUNIT_ASSERT_TRUE(test, strcat(pad.buf, src) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last two bytes should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* Add one more character to the end. */
KUNIT_ASSERT_TRUE(test, strcat(pad.buf, one) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* And this one char will overflow. */
KUNIT_ASSERT_TRUE(test, strcat(pad.buf, one) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And adding two will overflow more. */
KUNIT_ASSERT_TRUE(test, strcat(pad.buf, two) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strncat_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf)] = { };
int i, partial;
/* Fill 31 bytes with valid characters. */
partial = sizeof(src) / 2 - 1;
for (i = 0; i < partial; i++)
src[i] = i + 'A';
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strncat() using less than half max size. */
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, partial) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Legitimate strncat() now 2 bytes shy of end. */
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, partial) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last two bytes should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* Add one more character to the end. */
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* And this one char will overflow. */
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And adding two will overflow more. */
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 2) == pad.buf);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Force an unterminated destination, and overflow. */
pad.buf[sizeof(pad.buf) - 1] = 'A';
KUNIT_ASSERT_TRUE(test, strncat(pad.buf, src, 1) == pad.buf);
/* This will have tripped both strlen() and strcat(). */
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 3);
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* But we should not go beyond the end. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strlcat_test(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf)] = { };
int i, partial;
int len = sizeof(pad.buf) + unconst;
/* Fill 15 bytes with valid characters. */
partial = sizeof(src) / 2 - 1;
for (i = 0; i < partial; i++)
src[i] = i + 'A';
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strlcat() using less than half max size. */
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len), partial);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Legitimate strlcat() now 2 bytes shy of end. */
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len), partial * 2);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last two bytes should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* Add one more character to the end. */
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "Q", len), partial * 2 + 1);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* And this one char will overflow. */
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "V", len * 2), len);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And adding two will overflow more. */
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "QQ", len * 2), len + 1);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
/* Last byte should be %NUL thanks to FORTIFY. */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Force an unterminated destination, and overflow. */
pad.buf[sizeof(pad.buf) - 1] = 'A';
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, "TT", len * 2), len + 2);
/* This will have tripped both strlen() and strlcat(). */
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
/* But we should not go beyond the end. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Force an unterminated source, and overflow. */
memset(src, 'B', sizeof(src));
pad.buf[sizeof(pad.buf) - 1] = '\0';
KUNIT_ASSERT_EQ(test, strlcat(pad.buf, src, len * 3), len - 1 + sizeof(src));
/* This will have tripped both strlen() and strlcat(). */
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 3);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 3);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
/* But we should not go beyond the end. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void memscan_test(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
char needle = 'm';
size_t len = sizeof(haystack) + unconst;
KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len),
mem);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Catch too-large range. */
KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len + 1),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len * 2),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memchr_test(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
char needle = 'm';
size_t len = sizeof(haystack) + unconst;
KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len),
mem);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Catch too-large range. */
KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len + 1),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len * 2),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memchr_inv_test(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + 1;
char needle = 'W';
size_t len = sizeof(haystack) + unconst;
/* Normal search is okay. */
KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len),
mem);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Catch too-large range. */
KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len + 1),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len * 2),
NULL);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memcmp_test(struct kunit *test)
{
char one[] = "My mind is going ...";
char two[] = "My mind is going ... I can feel it.";
size_t one_len = sizeof(one) + unconst - 1;
size_t two_len = sizeof(two) + unconst - 1;
/* We match the first string (ignoring the %NUL). */
KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len), 0);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Still in bounds, but no longer matching. */
KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len + 1), -32);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Catch too-large ranges. */
KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len + 2), INT_MIN);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_ASSERT_EQ(test, memcmp(two, one, two_len + 2), INT_MIN);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void kmemdup_test(struct kunit *test)
{
char src[] = "I got Doom running on it!";
char *copy;
size_t len = sizeof(src) + unconst;
/* Copy is within bounds. */
copy = kmemdup(src, len, GFP_KERNEL);
KUNIT_EXPECT_NOT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
kfree(copy);
/* Without %NUL. */
copy = kmemdup(src, len - 1, GFP_KERNEL);
KUNIT_EXPECT_NOT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
kfree(copy);
/* Tiny bounds. */
copy = kmemdup(src, 1, GFP_KERNEL);
KUNIT_EXPECT_NOT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
kfree(copy);
/* Out of bounds by 1 byte. */
copy = kmemdup(src, len + 1, GFP_KERNEL);
KUNIT_EXPECT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
kfree(copy);
/* Way out of bounds. */
copy = kmemdup(src, len * 2, GFP_KERNEL);
KUNIT_EXPECT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
kfree(copy);
/* Starting offset causing out of bounds. */
copy = kmemdup(src + 1, len, GFP_KERNEL);
KUNIT_EXPECT_NULL(test, copy);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 3);
kfree(copy);
}
static int fortify_test_init(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_FORTIFY_SOURCE))
kunit_skip(test, "Not built with CONFIG_FORTIFY_SOURCE=y");
fortify_read_overflows = 0;
kunit_add_named_resource(test, NULL, NULL, &read_resource,
"fortify_read_overflows",
&fortify_read_overflows);
fortify_write_overflows = 0;
kunit_add_named_resource(test, NULL, NULL, &write_resource,
"fortify_write_overflows",
&fortify_write_overflows);
return 0;
}
static struct kunit_case fortify_test_cases[] = {
KUNIT_CASE(known_sizes_test),
KUNIT_CASE(control_flow_split_test),
@ -319,11 +961,27 @@ static struct kunit_case fortify_test_cases[] = {
KUNIT_CASE(alloc_size_kvmalloc_dynamic_test),
KUNIT_CASE(alloc_size_devm_kmalloc_const_test),
KUNIT_CASE(alloc_size_devm_kmalloc_dynamic_test),
KUNIT_CASE(strlen_test),
KUNIT_CASE(strnlen_test),
KUNIT_CASE(strcpy_test),
KUNIT_CASE(strncpy_test),
KUNIT_CASE(strscpy_test),
KUNIT_CASE(strcat_test),
KUNIT_CASE(strncat_test),
KUNIT_CASE(strlcat_test),
/* skip memset: performs bounds checking on whole structs */
/* skip memcpy: still using warn-and-overwrite instead of hard-fail */
KUNIT_CASE(memscan_test),
KUNIT_CASE(memchr_test),
KUNIT_CASE(memchr_inv_test),
KUNIT_CASE(memcmp_test),
KUNIT_CASE(kmemdup_test),
{}
};
static struct kunit_suite fortify_test_suite = {
.name = "fortify",
.init = fortify_test_init,
.test_cases = fortify_test_cases,
};

View File

@ -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) { \

View File

@ -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 - \

View File

@ -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/unaligned.h>
#include <asm/byteorder.h>
#include <asm/word-at-a-time.h>
#include <asm/page.h>
#include <asm/rwonce.h>
#include <asm/unaligned.h>
#include <asm/word-at-a-time.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

View File

@ -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)
{
pr_emerg("detected buffer overflow in %s\n", name);
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)
{
__fortify_report(reason, avail, size);
BUG();
}
EXPORT_SYMBOL(fortify_panic);
EXPORT_SYMBOL(__fortify_panic);
#endif /* CONFIG_FORTIFY_SOURCE */

View File

@ -1,34 +1,25 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Test cases for lib/string_helpers.c module.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <kunit/test.h>
#include <linux/array_size.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
static __init bool test_string_check_buf(const char *name, unsigned int flags,
char *in, size_t p,
char *out_real, size_t q_real,
char *out_test, size_t q_test)
static void test_string_check_buf(struct kunit *test,
const char *name, unsigned int flags,
char *in, size_t p,
char *out_real, size_t q_real,
char *out_test, size_t q_test)
{
if (q_real == q_test && !memcmp(out_test, out_real, q_test))
return true;
pr_warn("Test '%s' failed: flags = %#x\n", name, flags);
print_hex_dump(KERN_WARNING, "Input: ", DUMP_PREFIX_NONE, 16, 1,
in, p, true);
print_hex_dump(KERN_WARNING, "Expected: ", DUMP_PREFIX_NONE, 16, 1,
out_test, q_test, true);
print_hex_dump(KERN_WARNING, "Got: ", DUMP_PREFIX_NONE, 16, 1,
out_real, q_real, true);
return false;
KUNIT_ASSERT_EQ_MSG(test, q_real, q_test, "name:%s", name);
KUNIT_EXPECT_MEMEQ_MSG(test, out_test, out_real, q_test,
"name:%s", name);
}
struct test_string {
@ -37,7 +28,7 @@ struct test_string {
unsigned int flags;
};
static const struct test_string strings[] __initconst = {
static const struct test_string strings[] = {
{
.in = "\\f\\ \\n\\r\\t\\v",
.out = "\f\\ \n\r\t\v",
@ -60,17 +51,19 @@ static const struct test_string strings[] __initconst = {
},
};
static void __init test_string_unescape(const char *name, unsigned int flags,
bool inplace)
static void test_string_unescape(struct kunit *test,
const char *name, unsigned int flags,
bool inplace)
{
int q_real = 256;
char *in = kmalloc(q_real, GFP_KERNEL);
char *out_test = kmalloc(q_real, GFP_KERNEL);
char *out_real = kmalloc(q_real, GFP_KERNEL);
char *in = kunit_kzalloc(test, q_real, GFP_KERNEL);
char *out_test = kunit_kzalloc(test, q_real, GFP_KERNEL);
char *out_real = kunit_kzalloc(test, q_real, GFP_KERNEL);
int i, p = 0, q_test = 0;
if (!in || !out_test || !out_real)
goto out;
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_real);
for (i = 0; i < ARRAY_SIZE(strings); i++) {
const char *s = strings[i].in;
@ -103,12 +96,8 @@ static void __init test_string_unescape(const char *name, unsigned int flags,
q_real = string_unescape(in, out_real, q_real, flags);
}
test_string_check_buf(name, flags, in, p - 1, out_real, q_real,
test_string_check_buf(test, name, flags, in, p - 1, out_real, q_real,
out_test, q_test);
out:
kfree(out_real);
kfree(out_test);
kfree(in);
}
struct test_string_1 {
@ -123,7 +112,7 @@ struct test_string_2 {
};
#define TEST_STRING_2_DICT_0 NULL
static const struct test_string_2 escape0[] __initconst = {{
static const struct test_string_2 escape0[] = {{
.in = "\f\\ \n\r\t\v",
.s1 = {{
.out = "\\f\\ \\n\\r\\t\\v",
@ -221,7 +210,7 @@ static const struct test_string_2 escape0[] __initconst = {{
}};
#define TEST_STRING_2_DICT_1 "b\\ \t\r\xCF"
static const struct test_string_2 escape1[] __initconst = {{
static const struct test_string_2 escape1[] = {{
.in = "\f\\ \n\r\t\v",
.s1 = {{
.out = "\f\\134\\040\n\\015\\011\v",
@ -358,7 +347,7 @@ static const struct test_string_2 escape1[] __initconst = {{
/* terminator */
}};
static const struct test_string strings_upper[] __initconst = {
static const struct test_string strings_upper[] = {
{
.in = "abcdefgh1234567890test",
.out = "ABCDEFGH1234567890TEST",
@ -369,7 +358,7 @@ static const struct test_string strings_upper[] __initconst = {
},
};
static const struct test_string strings_lower[] __initconst = {
static const struct test_string strings_lower[] = {
{
.in = "ABCDEFGH1234567890TEST",
.out = "abcdefgh1234567890test",
@ -380,8 +369,8 @@ static const struct test_string strings_lower[] __initconst = {
},
};
static __init const char *test_string_find_match(const struct test_string_2 *s2,
unsigned int flags)
static const char *test_string_find_match(const struct test_string_2 *s2,
unsigned int flags)
{
const struct test_string_1 *s1 = s2->s1;
unsigned int i;
@ -402,31 +391,31 @@ static __init const char *test_string_find_match(const struct test_string_2 *s2,
return NULL;
}
static __init void
test_string_escape_overflow(const char *in, int p, unsigned int flags, const char *esc,
static void
test_string_escape_overflow(struct kunit *test,
const char *in, int p, unsigned int flags, const char *esc,
int q_test, const char *name)
{
int q_real;
q_real = string_escape_mem(in, p, NULL, 0, flags, esc);
if (q_real != q_test)
pr_warn("Test '%s' failed: flags = %#x, osz = 0, expected %d, got %d\n",
name, flags, q_test, q_real);
KUNIT_EXPECT_EQ_MSG(test, q_real, q_test, "name:%s: flags:%#x", name, flags);
}
static __init void test_string_escape(const char *name,
const struct test_string_2 *s2,
unsigned int flags, const char *esc)
static void test_string_escape(struct kunit *test, const char *name,
const struct test_string_2 *s2,
unsigned int flags, const char *esc)
{
size_t out_size = 512;
char *out_test = kmalloc(out_size, GFP_KERNEL);
char *out_real = kmalloc(out_size, GFP_KERNEL);
char *in = kmalloc(256, GFP_KERNEL);
char *out_test = kunit_kzalloc(test, out_size, GFP_KERNEL);
char *out_real = kunit_kzalloc(test, out_size, GFP_KERNEL);
char *in = kunit_kzalloc(test, 256, GFP_KERNEL);
int p = 0, q_test = 0;
int q_real;
if (!out_test || !out_real || !in)
goto out;
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_real);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in);
for (; s2->in; s2++) {
const char *out;
@ -462,62 +451,99 @@ static __init void test_string_escape(const char *name,
q_real = string_escape_mem(in, p, out_real, out_size, flags, esc);
test_string_check_buf(name, flags, in, p, out_real, q_real, out_test,
test_string_check_buf(test, name, flags, in, p, out_real, q_real, out_test,
q_test);
test_string_escape_overflow(in, p, flags, esc, q_test, name);
out:
kfree(in);
kfree(out_real);
kfree(out_test);
test_string_escape_overflow(test, in, p, flags, esc, q_test, name);
}
#define string_get_size_maxbuf 16
#define test_string_get_size_one(size, blk_size, exp_result10, exp_result2) \
do { \
BUILD_BUG_ON(sizeof(exp_result10) >= string_get_size_maxbuf); \
BUILD_BUG_ON(sizeof(exp_result2) >= string_get_size_maxbuf); \
__test_string_get_size((size), (blk_size), (exp_result10), \
(exp_result2)); \
#define test_string_get_size_one(size, blk_size, exp_result10, exp_result2) \
do { \
BUILD_BUG_ON(sizeof(exp_result10) >= string_get_size_maxbuf); \
BUILD_BUG_ON(sizeof(exp_result2) >= string_get_size_maxbuf); \
__test_string_get_size(test, (size), (blk_size), (exp_result10), \
(exp_result2)); \
} while (0)
static __init void test_string_get_size_check(const char *units,
const char *exp,
char *res,
const u64 size,
const u64 blk_size)
static void test_string_get_size_check(struct kunit *test,
const char *units,
const char *exp,
char *res,
const u64 size,
const u64 blk_size)
{
if (!memcmp(res, exp, strlen(exp) + 1))
return;
res[string_get_size_maxbuf - 1] = '\0';
pr_warn("Test 'test_string_get_size' failed!\n");
pr_warn("string_get_size(size = %llu, blk_size = %llu, units = %s)\n",
KUNIT_EXPECT_MEMEQ_MSG(test, res, exp, strlen(exp) + 1,
"string_get_size(size = %llu, blk_size = %llu, units = %s)",
size, blk_size, units);
pr_warn("expected: '%s', got '%s'\n", exp, res);
}
static __init void __test_string_get_size(const u64 size, const u64 blk_size,
const char *exp_result10,
const char *exp_result2)
static void __strchrcut(char *dst, const char *src, const char *cut)
{
const char *from = src;
size_t len;
do {
len = strcspn(from, cut);
memcpy(dst, from, len);
dst += len;
from += len;
} while (*from++);
*dst = '\0';
}
static void __test_string_get_size_one(struct kunit *test,
const u64 size, const u64 blk_size,
const char *exp_result10,
const char *exp_result2,
enum string_size_units units,
const char *cut)
{
char buf10[string_get_size_maxbuf];
char buf2[string_get_size_maxbuf];
char exp10[string_get_size_maxbuf];
char exp2[string_get_size_maxbuf];
char prefix10[64];
char prefix2[64];
string_get_size(size, blk_size, STRING_UNITS_10, buf10, sizeof(buf10));
string_get_size(size, blk_size, STRING_UNITS_2, buf2, sizeof(buf2));
sprintf(prefix10, "STRING_UNITS_10 [%s]", cut);
sprintf(prefix2, "STRING_UNITS_2 [%s]", cut);
test_string_get_size_check("STRING_UNITS_10", exp_result10, buf10,
size, blk_size);
__strchrcut(exp10, exp_result10, cut);
__strchrcut(exp2, exp_result2, cut);
test_string_get_size_check("STRING_UNITS_2", exp_result2, buf2,
size, blk_size);
string_get_size(size, blk_size, STRING_UNITS_10 | units, buf10, sizeof(buf10));
string_get_size(size, blk_size, STRING_UNITS_2 | units, buf2, sizeof(buf2));
test_string_get_size_check(test, prefix10, exp10, buf10, size, blk_size);
test_string_get_size_check(test, prefix2, exp2, buf2, size, blk_size);
}
static __init void test_string_get_size(void)
static void __test_string_get_size(struct kunit *test,
const u64 size, const u64 blk_size,
const char *exp_result10,
const char *exp_result2)
{
struct {
enum string_size_units units;
const char *cut;
} get_size_test_cases[] = {
{ 0, "" },
{ STRING_UNITS_NO_SPACE, " " },
{ STRING_UNITS_NO_SPACE | STRING_UNITS_NO_BYTES, " B" },
{ STRING_UNITS_NO_BYTES, "B" },
};
int i;
for (i = 0; i < ARRAY_SIZE(get_size_test_cases); i++)
__test_string_get_size_one(test, size, blk_size,
exp_result10, exp_result2,
get_size_test_cases[i].units,
get_size_test_cases[i].cut);
}
static void test_get_size(struct kunit *test)
{
/* small values */
test_string_get_size_one(0, 512, "0 B", "0 B");
@ -537,7 +563,7 @@ static __init void test_string_get_size(void)
test_string_get_size_one(4096, U64_MAX, "75.6 ZB", "64.0 ZiB");
}
static void __init test_string_upper_lower(void)
static void test_upper_lower(struct kunit *test)
{
char *dst;
int i;
@ -547,16 +573,10 @@ static void __init test_string_upper_lower(void)
int len = strlen(strings_upper[i].in) + 1;
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return;
KUNIT_ASSERT_NOT_NULL(test, dst);
string_upper(dst, s);
if (memcmp(dst, strings_upper[i].out, len)) {
pr_warn("Test 'string_upper' failed : expected %s, got %s!\n",
strings_upper[i].out, dst);
kfree(dst);
return;
}
KUNIT_EXPECT_STREQ(test, dst, strings_upper[i].out);
kfree(dst);
}
@ -565,45 +585,44 @@ static void __init test_string_upper_lower(void)
int len = strlen(strings_lower[i].in) + 1;
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return;
KUNIT_ASSERT_NOT_NULL(test, dst);
string_lower(dst, s);
if (memcmp(dst, strings_lower[i].out, len)) {
pr_warn("Test 'string_lower failed : : expected %s, got %s!\n",
strings_lower[i].out, dst);
kfree(dst);
return;
}
KUNIT_EXPECT_STREQ(test, dst, strings_lower[i].out);
kfree(dst);
}
}
static int __init test_string_helpers_init(void)
static void test_unescape(struct kunit *test)
{
unsigned int i;
pr_info("Running tests...\n");
for (i = 0; i < UNESCAPE_ALL_MASK + 1; i++)
test_string_unescape("unescape", i, false);
test_string_unescape("unescape inplace",
test_string_unescape(test, "unescape", i, false);
test_string_unescape(test, "unescape inplace",
get_random_u32_below(UNESCAPE_ALL_MASK + 1), true);
/* Without dictionary */
for (i = 0; i < ESCAPE_ALL_MASK + 1; i++)
test_string_escape("escape 0", escape0, i, TEST_STRING_2_DICT_0);
test_string_escape(test, "escape 0", escape0, i, TEST_STRING_2_DICT_0);
/* With dictionary */
for (i = 0; i < ESCAPE_ALL_MASK + 1; i++)
test_string_escape("escape 1", escape1, i, TEST_STRING_2_DICT_1);
/* Test string_get_size() */
test_string_get_size();
/* Test string upper(), string_lower() */
test_string_upper_lower();
return -EINVAL;
test_string_escape(test, "escape 1", escape1, i, TEST_STRING_2_DICT_1);
}
module_init(test_string_helpers_init);
static struct kunit_case string_helpers_test_cases[] = {
KUNIT_CASE(test_get_size),
KUNIT_CASE(test_upper_lower),
KUNIT_CASE(test_unescape),
{}
};
static struct kunit_suite string_helpers_test_suite = {
.name = "string_helpers",
.test_cases = string_helpers_test_cases,
};
kunit_test_suites(&string_helpers_test_suite);
MODULE_LICENSE("Dual BSD/GPL");

199
lib/string_kunit.c Normal file
View File

@ -0,0 +1,199 @@
// 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 void test_memset16(struct kunit *test)
{
unsigned i, j, k;
u16 v, *p;
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++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset16(p + i, 0xb1b2, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2,
"i:%d j:%d k:%d", i, j, k);
} else {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
}
static void test_memset32(struct kunit *test)
{
unsigned i, j, k;
u32 v, *p;
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++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset32(p + i, 0xb1b2b3b4, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2b3b4,
"i:%d j:%d k:%d", i, j, k);
} else {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
}
static void test_memset64(struct kunit *test)
{
unsigned i, j, k;
u64 v, *p;
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++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset64(p + i, 0xb1b2b3b4b5b6b7b8ULL, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1a1a1a1a1ULL,
"i:%d j:%d k:%d", i, j, k);
} else if (k < i + j) {
KUNIT_ASSERT_EQ_MSG(test, v, 0xb1b2b3b4b5b6b7b8ULL,
"i:%d j:%d k:%d", i, j, k);
} else {
KUNIT_ASSERT_EQ_MSG(test, v, 0xa1a1a1a1a1a1a1a1ULL,
"i:%d j:%d k:%d", i, j, k);
}
}
}
}
}
static void test_strchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
char *result;
int i;
for (i = 0; i < strlen(test_string) + 1; i++) {
result = strchr(test_string, test_string[i]);
KUNIT_ASSERT_EQ_MSG(test, result - test_string, i,
"char:%c", 'a' + i);
}
result = strchr(empty_string, '\0');
KUNIT_ASSERT_PTR_EQ(test, result, empty_string);
result = strchr(empty_string, 'a');
KUNIT_ASSERT_NULL(test, result);
result = strchr(test_string, 'z');
KUNIT_ASSERT_NULL(test, result);
}
static void test_strnchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
char *result;
int i, j;
for (i = 0; i < strlen(test_string) + 1; i++) {
for (j = 0; j < strlen(test_string) + 2; j++) {
result = strnchr(test_string, j, test_string[i]);
if (j <= i) {
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);
}
}
}
result = strnchr(empty_string, 0, '\0');
KUNIT_ASSERT_NULL(test, result);
result = strnchr(empty_string, 1, '\0');
KUNIT_ASSERT_PTR_EQ(test, result, empty_string);
result = strnchr(empty_string, 1, 'a');
KUNIT_ASSERT_NULL(test, result);
result = strnchr(NULL, 0, '\0');
KUNIT_ASSERT_NULL(test, result);
}
static void test_strspn(struct kunit *test)
{
static const struct strspn_test {
const char str[16];
const char accept[16];
const char reject[16];
unsigned a;
unsigned r;
} 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;
for (i = 0; i < ARRAY_SIZE(tests); ++i, ++s) {
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);
}
}
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 struct kunit_suite string_test_suite = {
.name = "string",
.test_cases = string_test_cases,
};
kunit_test_suites(&string_test_suite);
MODULE_LICENSE("GPL v2");

View File

@ -1,257 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
static __init int memset16_selftest(void)
{
unsigned i, j, k;
u16 v, *p;
p = kmalloc(256 * 2 * 2, GFP_KERNEL);
if (!p)
return -1;
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset16(p + i, 0xb1b2, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1)
goto fail;
} else if (k < i + j) {
if (v != 0xb1b2)
goto fail;
} else {
if (v != 0xa1a1)
goto fail;
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int memset32_selftest(void)
{
unsigned i, j, k;
u32 v, *p;
p = kmalloc(256 * 2 * 4, GFP_KERNEL);
if (!p)
return -1;
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset32(p + i, 0xb1b2b3b4, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1a1a1)
goto fail;
} else if (k < i + j) {
if (v != 0xb1b2b3b4)
goto fail;
} else {
if (v != 0xa1a1a1a1)
goto fail;
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int memset64_selftest(void)
{
unsigned i, j, k;
u64 v, *p;
p = kmalloc(256 * 2 * 8, GFP_KERNEL);
if (!p)
return -1;
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
memset(p, 0xa1, 256 * 2 * sizeof(v));
memset64(p + i, 0xb1b2b3b4b5b6b7b8ULL, j);
for (k = 0; k < 512; k++) {
v = p[k];
if (k < i) {
if (v != 0xa1a1a1a1a1a1a1a1ULL)
goto fail;
} else if (k < i + j) {
if (v != 0xb1b2b3b4b5b6b7b8ULL)
goto fail;
} else {
if (v != 0xa1a1a1a1a1a1a1a1ULL)
goto fail;
}
}
}
}
fail:
kfree(p);
if (i < 256)
return (i << 24) | (j << 16) | k | 0x8000;
return 0;
}
static __init int strchr_selftest(void)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
char *result;
int i;
for (i = 0; i < strlen(test_string) + 1; i++) {
result = strchr(test_string, test_string[i]);
if (result - test_string != i)
return i + 'a';
}
result = strchr(empty_string, '\0');
if (result != empty_string)
return 0x101;
result = strchr(empty_string, 'a');
if (result)
return 0x102;
result = strchr(test_string, 'z');
if (result)
return 0x103;
return 0;
}
static __init int strnchr_selftest(void)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
char *result;
int i, j;
for (i = 0; i < strlen(test_string) + 1; i++) {
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;
}
if (result - test_string != i)
return ((i + 'a') << 8) | j;
}
}
result = strnchr(empty_string, 0, '\0');
if (result)
return 0x10001;
result = strnchr(empty_string, 1, '\0');
if (result != empty_string)
return 0x10002;
result = strnchr(empty_string, 1, 'a');
if (result)
return 0x10003;
result = strnchr(NULL, 0, '\0');
if (result)
return 0x10004;
return 0;
}
static __init int strspn_selftest(void)
{
static const struct strspn_test {
const char str[16];
const char accept[16];
const char reject[16];
unsigned a;
unsigned r;
} tests[] __initconst = {
{ "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;
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;
}
return 0;
}
static __exit void string_selftest_remove(void)
{
}
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;
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;
}
module_init(string_selftest_init);
module_exit(string_selftest_remove);
MODULE_LICENSE("GPL v2");

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-only
/// Find places to use string_choices.h's various helpers.
//
// Confidence: Medium
// Options: --no-includes --include-headers
virtual patch
virtual context
virtual report
@str_plural depends on patch@
expression E;
@@
(
- ((E == 1) ? "" : "s")
+ str_plural(E)
|
- ((E != 1) ? "s" : "")
+ str_plural(E)
|
- ((E > 1) ? "s" : "")
+ str_plural(E)
)
@str_plural_r depends on !patch exists@
expression E;
position P;
@@
(
* ((E@P == 1) ? "" : "s")
|
* ((E@P != 1) ? "s" : "")
|
* ((E@P > 1) ? "s" : "")
)
@script:python depends on report@
p << str_plural_r.P;
e << str_plural_r.E;
@@
coccilib.report.print_report(p[0], "opportunity for str_plural(%s)" % e)

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0-only
///
/// Check for code that could use struct_size().
///
// Confidence: Medium
// Author: Jacob Keller <jacob.e.keller@intel.com>
// Copyright: (C) 2023 Intel Corporation
// Options: --no-includes --include-headers
virtual patch
virtual context
virtual org
virtual report
// the overflow Kunit tests have some code which intentionally does not use
// the macros, so we want to ignore this code when reporting potential
// issues.
@overflow_tests@
identifier f = overflow_size_helpers_test;
@@
f
//----------------------------------------------------------
// For context mode
//----------------------------------------------------------
@depends on !overflow_tests && context@
expression E1, E2;
identifier m;
@@
(
* (sizeof(*E1) + (E2 * sizeof(*E1->m)))
)
//----------------------------------------------------------
// For patch mode
//----------------------------------------------------------
@depends on !overflow_tests && patch@
expression E1, E2;
identifier m;
@@
(
- (sizeof(*E1) + (E2 * sizeof(*E1->m)))
+ struct_size(E1, m, E2)
)
//----------------------------------------------------------
// For org and report mode
//----------------------------------------------------------
@r depends on !overflow_tests && (org || report)@
expression E1, E2;
identifier m;
position p;
@@
(
(sizeof(*E1)@p + (E2 * sizeof(*E1->m)))
)
@script:python depends on org@
p << r.p;
@@
coccilib.org.print_todo(p[0], "WARNING should use struct_size")
@script:python depends on report@
p << r.p;
@@
msg="WARNING: Use struct_size"
coccilib.report.print_report(p[0], msg)

View File

@ -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");
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -0,0 +1 @@
../../../../../../include/linux/wordpart.h