Merge 87caef4220 ("Merge tag 'hardening-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux") into android-mainline

Steps on the way to 6.10-rc1

Change-Id: Ic16b90c2a414259ef399577fcd12e2781c069032
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Greg Kroah-Hartman
2024-07-06 11:39:20 +00:00
committed by Treehugger Robot
27 changed files with 768 additions and 477 deletions
+1 -2
View File
@@ -8463,8 +8463,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/har
F: include/linux/fortify-string.h
F: lib/fortify_kunit.c
F: lib/memcpy_kunit.c
F: lib/strcat_kunit.c
F: lib/strscpy_kunit.c
F: lib/test_fortify/*
F: scripts/test_fortify.sh
K: \b__NO_FORTIFY\b
@@ -22698,6 +22696,7 @@ F: include/linux/ubsan.h
F: lib/Kconfig.ubsan
F: lib/test_ubsan.c
F: lib/ubsan.c
F: lib/ubsan.h
F: scripts/Makefile.ubsan
K: \bARCH_HAS_UBSAN\b
+1
View File
@@ -5,6 +5,7 @@ CONFIG_ARM64_SW_TTBR0_PAN=y
# Software Shadow Stack or PAC
CONFIG_SHADOW_CALL_STACK=y
CONFIG_UNWIND_PATCH_PAC_INTO_SCS=y
# Pointer authentication (ARMv8.3 and later). If hardware actually supports
# it, one can turn off CONFIG_STACKPROTECTOR_STRONG with this enabled.
+3
View File
@@ -10,5 +10,8 @@ CONFIG_INTEL_IOMMU_DEFAULT_ON=y
CONFIG_INTEL_IOMMU_SVM=y
CONFIG_AMD_IOMMU=y
# Enforce CET Indirect Branch Tracking in the kernel.
CONFIG_X86_KERNEL_IBT=y
# Enable CET Shadow Stack for userspace.
CONFIG_X86_USER_SHADOW_STACK=y
+5 -9
View File
@@ -2964,17 +2964,13 @@ mptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc,
goto out_free;
manufacture_reply = data_out + sizeof(struct rep_manu_request);
strscpy(edev->vendor_id, manufacture_reply->vendor_id,
sizeof(edev->vendor_id));
strscpy(edev->product_id, manufacture_reply->product_id,
sizeof(edev->product_id));
strscpy(edev->product_rev, manufacture_reply->product_rev,
sizeof(edev->product_rev));
memtostr(edev->vendor_id, manufacture_reply->vendor_id);
memtostr(edev->product_id, manufacture_reply->product_id);
memtostr(edev->product_rev, manufacture_reply->product_rev);
edev->level = manufacture_reply->sas_format;
if (manufacture_reply->sas_format) {
strscpy(edev->component_vendor_id,
manufacture_reply->component_vendor_id,
sizeof(edev->component_vendor_id));
memtostr(edev->component_vendor_id,
manufacture_reply->component_vendor_id);
tmp = (u8 *)&manufacture_reply->component_id;
edev->component_id = tmp[0] << 8 | tmp[1];
edev->component_revision_id =
+1 -1
View File
@@ -19,7 +19,7 @@ KASAN_SANITIZE_rodata.o := n
KCSAN_SANITIZE_rodata.o := n
KCOV_INSTRUMENT_rodata.o := n
OBJECT_FILES_NON_STANDARD_rodata.o := y
CFLAGS_REMOVE_rodata.o += $(CC_FLAGS_LTO) $(RETHUNK_CFLAGS)
CFLAGS_REMOVE_rodata.o += $(CC_FLAGS_LTO) $(RETHUNK_CFLAGS) $(CC_FLAGS_CFI)
OBJCOPYFLAGS :=
OBJCOPYFLAGS_rodata_objcopy.o := \
+1 -1
View File
@@ -61,7 +61,7 @@ static void *setup_function_descriptor(func_desc_t *fdesc, void *dst)
return fdesc;
}
static noinline void execute_location(void *dst, bool write)
static noinline __nocfi void execute_location(void *dst, bool write)
{
void (*func)(void);
func_desc_t fdesc;
+5 -9
View File
@@ -209,17 +209,13 @@ static int mpi3mr_report_manufacture(struct mpi3mr_ioc *mrioc,
goto out;
}
strscpy(edev->vendor_id, manufacture_reply->vendor_id,
SAS_EXPANDER_VENDOR_ID_LEN);
strscpy(edev->product_id, manufacture_reply->product_id,
SAS_EXPANDER_PRODUCT_ID_LEN);
strscpy(edev->product_rev, manufacture_reply->product_rev,
SAS_EXPANDER_PRODUCT_REV_LEN);
memtostr(edev->vendor_id, manufacture_reply->vendor_id);
memtostr(edev->product_id, manufacture_reply->product_id);
memtostr(edev->product_rev, manufacture_reply->product_rev);
edev->level = manufacture_reply->sas_format & 1;
if (edev->level) {
strscpy(edev->component_vendor_id,
manufacture_reply->component_vendor_id,
SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
memtostr(edev->component_vendor_id,
manufacture_reply->component_vendor_id);
tmp = (u8 *)&manufacture_reply->component_id;
edev->component_id = tmp[0] << 8 | tmp[1];
edev->component_revision_id =
+2 -4
View File
@@ -1909,10 +1909,8 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type)
if (fx_type == FXDISC_GET_CONFIG_INFO) {
struct config_info_data *pinfo =
(struct config_info_data *) fdisc->u.fxiocb.rsp_addr;
strscpy(vha->hw->model_number, pinfo->model_num,
ARRAY_SIZE(vha->hw->model_number));
strscpy(vha->hw->model_desc, pinfo->model_description,
ARRAY_SIZE(vha->hw->model_desc));
memtostr(vha->hw->model_number, pinfo->model_num);
memtostr(vha->hw->model_desc, pinfo->model_description);
memcpy(&vha->hw->mr.symbolic_name, pinfo->symbolic_name,
sizeof(vha->hw->mr.symbolic_name));
memcpy(&vha->hw->mr.serial_num, pinfo->serial_num,
+1 -1
View File
@@ -433,7 +433,7 @@ struct acrn_ioreq_client *acrn_ioreq_client_create(struct acrn_vm *vm,
client->priv = priv;
client->is_default = is_default;
if (name)
strncpy(client->name, name, sizeof(client->name) - 1);
strscpy(client->name, name);
rwlock_init(&client->range_lock);
INIT_LIST_HEAD(&client->range_list);
init_waitqueue_head(&client->wq);
+1 -3
View File
@@ -1606,9 +1606,7 @@ ecryptfs_add_new_key_tfm(struct ecryptfs_key_tfm **key_tfm, char *cipher_name,
goto out;
}
mutex_init(&tmp_tfm->key_tfm_mutex);
strncpy(tmp_tfm->cipher_name, cipher_name,
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
tmp_tfm->cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
strscpy(tmp_tfm->cipher_name, cipher_name);
tmp_tfm->key_size = key_size;
rc = ecryptfs_process_key_cipher(&tmp_tfm->key_tfm,
tmp_tfm->cipher_name,
+6 -20
View File
@@ -256,11 +256,8 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
substring_t args[MAX_OPT_ARGS];
int token;
char *sig_src;
char *cipher_name_dst;
char *cipher_name_src;
char *fn_cipher_name_dst;
char *fn_cipher_name_src;
char *fnek_dst;
char *fnek_src;
char *cipher_key_bytes_src;
char *fn_cipher_key_bytes_src;
@@ -293,12 +290,8 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
case ecryptfs_opt_cipher:
case ecryptfs_opt_ecryptfs_cipher:
cipher_name_src = args[0].from;
cipher_name_dst =
mount_crypt_stat->
global_default_cipher_name;
strncpy(cipher_name_dst, cipher_name_src,
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
cipher_name_dst[ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
strscpy(mount_crypt_stat->global_default_cipher_name,
cipher_name_src);
cipher_name_set = 1;
break;
case ecryptfs_opt_ecryptfs_key_bytes:
@@ -326,11 +319,8 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
break;
case ecryptfs_opt_fnek_sig:
fnek_src = args[0].from;
fnek_dst =
mount_crypt_stat->global_default_fnek_sig;
strncpy(fnek_dst, fnek_src, ECRYPTFS_SIG_SIZE_HEX);
mount_crypt_stat->global_default_fnek_sig[
ECRYPTFS_SIG_SIZE_HEX] = '\0';
strscpy(mount_crypt_stat->global_default_fnek_sig,
fnek_src);
rc = ecryptfs_add_global_auth_tok(
mount_crypt_stat,
mount_crypt_stat->global_default_fnek_sig,
@@ -348,12 +338,8 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
break;
case ecryptfs_opt_fn_cipher:
fn_cipher_name_src = args[0].from;
fn_cipher_name_dst =
mount_crypt_stat->global_default_fn_cipher_name;
strncpy(fn_cipher_name_dst, fn_cipher_name_src,
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
mount_crypt_stat->global_default_fn_cipher_name[
ECRYPTFS_MAX_CIPHER_NAME_SIZE] = '\0';
strscpy(mount_crypt_stat->global_default_fn_cipher_name,
fn_cipher_name_src);
fn_cipher_name_set = 1;
break;
case ecryptfs_opt_fn_cipher_key_bytes:
+10 -12
View File
@@ -400,21 +400,19 @@ static int name_len(const char *xattr_name, int xattr_name_len)
return len;
}
static int copy_name(char *buffer, const char *xattr_name, int name_len)
static ssize_t copy_name(char *buffer, const char *xattr_name, int name_len)
{
int len = name_len;
int offset = 0;
ssize_t len;
if (!is_known_namespace(xattr_name)) {
memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
offset += XATTR_MAC_OSX_PREFIX_LEN;
len += XATTR_MAC_OSX_PREFIX_LEN;
}
strncpy(buffer + offset, xattr_name, name_len);
memset(buffer + offset + name_len, 0, 1);
len += 1;
if (!is_known_namespace(xattr_name))
len = scnprintf(buffer, name_len + XATTR_MAC_OSX_PREFIX_LEN,
"%s%s", XATTR_MAC_OSX_PREFIX, xattr_name);
else
len = strscpy(buffer, xattr_name, name_len + 1);
/* include NUL-byte in length for non-empty name */
if (len >= 0)
len++;
return len;
}
+3 -10
View File
@@ -389,16 +389,9 @@ static void direntry_print_item(struct item_head *ih, char *item)
name = item + deh_location(deh);
if (name[namelen - 1] == 0)
namelen = strlen(name);
namebuf[0] = '"';
if (namelen > sizeof(namebuf) - 3) {
strncpy(namebuf + 1, name, sizeof(namebuf) - 3);
namebuf[sizeof(namebuf) - 2] = '"';
namebuf[sizeof(namebuf) - 1] = 0;
} else {
memcpy(namebuf + 1, name, namelen);
namebuf[namelen + 1] = '"';
namebuf[namelen + 2] = 0;
}
scnprintf(namebuf, sizeof(namebuf), "\"%.*s\"",
(int)sizeof(namebuf)-3, name);
printk("%d: %-15s%-15d%-15d%-15lld%-15lld(%s)\n",
i, namebuf,
+7 -2
View File
@@ -15,10 +15,14 @@
#define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \
FIELD_PREP(GENMASK(7, 1), func))
/* Overridden by KUnit tests. */
#ifndef fortify_panic
# define fortify_panic(func, write, avail, size, retfail) \
__fortify_panic(FORTIFY_REASON(func, write), avail, size)
#endif
#ifndef fortify_warn_once
# define fortify_warn_once(x...) WARN_ONCE(x)
#endif
#define FORTIFY_READ 0
#define FORTIFY_WRITE 1
@@ -609,7 +613,7 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
const size_t __q_size = (q_size); \
const size_t __p_size_field = (p_size_field); \
const size_t __q_size_field = (q_size_field); \
WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size, \
fortify_warn_once(fortify_memcpy_chk(__fortify_size, __p_size, \
__q_size, __p_size_field, \
__q_size_field, FORTIFY_FUNC_ ##op), \
#op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \
@@ -734,7 +738,8 @@ __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(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size, NULL);
fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size,
__real_kmemdup(p, 0, gfp));
return __real_kmemdup(p, size, gfp);
}
+57 -5
View File
@@ -14,8 +14,8 @@
#include <uapi/linux/string.h>
extern char *strndup_user(const char __user *, long);
extern void *memdup_user(const void __user *, size_t);
extern void *vmemdup_user(const void __user *, size_t);
extern void *memdup_user(const void __user *, size_t) __realloc_size(2);
extern void *vmemdup_user(const void __user *, size_t) __realloc_size(2);
extern void *memdup_user_nul(const void __user *, size_t);
/**
@@ -27,7 +27,8 @@ extern void *memdup_user_nul(const void __user *, size_t);
* Return: an ERR_PTR() on failure. Result is physically
* contiguous, to be freed by kfree().
*/
static inline void *memdup_array_user(const void __user *src, size_t n, size_t size)
static inline __realloc_size(2, 3)
void *memdup_array_user(const void __user *src, size_t n, size_t size)
{
size_t nbytes;
@@ -46,7 +47,8 @@ static inline void *memdup_array_user(const void __user *src, size_t n, size_t s
* Return: an ERR_PTR() on failure. Result may be not
* physically contiguous. Use kvfree() to free.
*/
static inline void *vmemdup_array_user(const void __user *src, size_t n, size_t size)
static inline __realloc_size(2, 3)
void *vmemdup_array_user(const void __user *src, size_t n, size_t size)
{
size_t nbytes;
@@ -285,7 +287,8 @@ extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
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);
extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp)
__realloc_size(2, 3);
/* lib/argv_split.c */
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
@@ -422,6 +425,55 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
memcpy(dest, src, strnlen(src, min(_src_len, _dest_len))); \
} while (0)
/**
* memtostr - Copy a possibly non-NUL-term string to a NUL-term string
* @dest: Pointer to destination NUL-terminates string
* @src: Pointer to character array (likely marked as __nonstring)
*
* This is a replacement for strncpy() uses where the source is not
* a NUL-terminated string.
*
* Note that sizes of @dest and @src must be known at compile-time.
*/
#define memtostr(dest, src) do { \
const size_t _dest_len = __builtin_object_size(dest, 1); \
const size_t _src_len = __builtin_object_size(src, 1); \
const size_t _src_chars = strnlen(src, _src_len); \
const size_t _copy_len = min(_dest_len - 1, _src_chars); \
\
BUILD_BUG_ON(!__builtin_constant_p(_dest_len) || \
!__builtin_constant_p(_src_len) || \
_dest_len == 0 || _dest_len == (size_t)-1 || \
_src_len == 0 || _src_len == (size_t)-1); \
memcpy(dest, src, _copy_len); \
dest[_copy_len] = '\0'; \
} while (0)
/**
* memtostr_pad - Copy a possibly non-NUL-term string to a NUL-term string
* with NUL padding in the destination
* @dest: Pointer to destination NUL-terminates string
* @src: Pointer to character array (likely marked as __nonstring)
*
* This is a replacement for strncpy() uses where the source is not
* a NUL-terminated string.
*
* Note that sizes of @dest and @src must be known at compile-time.
*/
#define memtostr_pad(dest, src) do { \
const size_t _dest_len = __builtin_object_size(dest, 1); \
const size_t _src_len = __builtin_object_size(src, 1); \
const size_t _src_chars = strnlen(src, _src_len); \
const size_t _copy_len = min(_dest_len - 1, _src_chars); \
\
BUILD_BUG_ON(!__builtin_constant_p(_dest_len) || \
!__builtin_constant_p(_src_len) || \
_dest_len == 0 || _dest_len == (size_t)-1 || \
_src_len == 0 || _src_len == (size_t)-1); \
memcpy(dest, src, _copy_len); \
memset(&dest[_copy_len], 0, _dest_len - _copy_len); \
} while (0)
/**
* memset_after - Set a value after a struct member to the end of a struct
*
+8
View File
@@ -55,4 +55,12 @@
#define __counted_by(m)
#endif
#ifndef __counted_by_le
#define __counted_by_le(m)
#endif
#ifndef __counted_by_be
#define __counted_by_be(m)
#endif
#endif /* _UAPI_LINUX_STDDEF_H */
+1 -2
View File
@@ -159,8 +159,7 @@ static int __init do_mount_root(const char *name, const char *fs,
if (!p)
return -ENOMEM;
data_page = page_address(p);
/* zero-pad. init_mount() will make sure it's terminated */
strncpy(data_page, data, PAGE_SIZE);
strscpy_pad(data_page, data, PAGE_SIZE);
}
ret = init_mount(name, "/root", fs, flags, data_page);
+8
View File
@@ -23,6 +23,10 @@ CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
CONFIG_RANDOM_KMALLOC_CACHES=y
# Sanity check userspace page table mappings.
CONFIG_PAGE_TABLE_CHECK=y
CONFIG_PAGE_TABLE_CHECK_ENFORCED=y
# Randomize kernel stack offset on syscall entry.
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
@@ -82,6 +86,10 @@ CONFIG_SECCOMP_FILTER=y
# Provides some protections against SYN flooding.
CONFIG_SYN_COOKIES=y
# Enable Kernel Control Flow Integrity (currently Clang only).
CONFIG_CFI_CLANG=y
# CONFIG_CFI_PERMISSIVE is not set
# Attack surface reduction: do not autoload TTY line disciplines.
# CONFIG_LDISC_AUTOLOAD is not set
+3 -3
View File
@@ -27,10 +27,10 @@ static int stack_erasing_sysctl(struct ctl_table *table, int write,
int ret = 0;
int state = !static_branch_unlikely(&stack_erasing_bypass);
int prev_state = state;
struct ctl_table table_copy = *table;
table->data = &state;
table->maxlen = sizeof(int);
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
table_copy.data = &state;
ret = proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
state = !!state;
if (ret || !write || state == prev_state)
return ret;
-10
View File
@@ -2759,16 +2759,6 @@ config HW_BREAKPOINT_KUNIT_TEST
If unsure, say N.
config STRCAT_KUNIT_TEST
tristate "Test strcat() family of functions at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config STRSCPY_KUNIT_TEST
tristate "Test strscpy*() family of functions at runtime" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config SIPHASH_KUNIT_TEST
tristate "Perform selftest on siphash functions" if !KUNIT_ALL_TESTS
depends on KUNIT
-2
View File
@@ -404,8 +404,6 @@ 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
obj-$(CONFIG_STRSCPY_KUNIT_TEST) += strscpy_kunit.o
obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
+167 -55
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Runtime test cases for CONFIG_FORTIFY_SOURCE. For testing memcpy(),
* see FORTIFY_MEM_* tests in LKDTM (drivers/misc/lkdtm/fortify.c).
* Runtime test cases for CONFIG_FORTIFY_SOURCE. For additional memcpy()
* testing see FORTIFY_MEM_* tests in LKDTM (drivers/misc/lkdtm/fortify.c).
*
* For corner cases with UBSAN, try testing with:
*
@@ -15,14 +15,31 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* We don't need to fill dmesg with the fortify WARNs during testing. */
#ifdef DEBUG
# define FORTIFY_REPORT_KUNIT(x...) __fortify_report(x)
# define FORTIFY_WARN_KUNIT(x...) WARN_ONCE(x)
#else
# define FORTIFY_REPORT_KUNIT(x...) do { } while (0)
# define FORTIFY_WARN_KUNIT(x...) do { } while (0)
#endif
/* 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_REPORT_KUNIT(FORTIFY_REASON(func, write), avail, size); \
fortify_add_kunit_error(write); \
return (retfail); \
} while (0)
/* Redefine fortify_warn_once() to track memcpy() failures. */
#define fortify_warn_once(chk_func, x...) do { \
bool __result = chk_func; \
FORTIFY_WARN_KUNIT(__result, x); \
if (__result) \
fortify_add_kunit_error(1); \
} while (0)
#include <kunit/device.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
@@ -64,7 +81,7 @@ void fortify_add_kunit_error(int write)
kunit_put_resource(resource);
}
static void known_sizes_test(struct kunit *test)
static void fortify_test_known_sizes(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, __compiletime_strlen("88888888"), 8);
KUNIT_EXPECT_EQ(test, __compiletime_strlen(array_of_10), 10);
@@ -97,7 +114,7 @@ static noinline size_t want_minus_one(int pick)
return __compiletime_strlen(str);
}
static void control_flow_split_test(struct kunit *test)
static void fortify_test_control_flow_split(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, want_minus_one(pick), SIZE_MAX);
}
@@ -173,11 +190,11 @@ static volatile size_t unknown_size = 50;
#endif
#define DEFINE_ALLOC_SIZE_TEST_PAIR(allocator) \
static void alloc_size_##allocator##_const_test(struct kunit *test) \
static void fortify_test_alloc_size_##allocator##_const(struct kunit *test) \
{ \
CONST_TEST_BODY(TEST_##allocator); \
} \
static void alloc_size_##allocator##_dynamic_test(struct kunit *test) \
static void fortify_test_alloc_size_##allocator##_dynamic(struct kunit *test) \
{ \
DYNAMIC_TEST_BODY(TEST_##allocator); \
}
@@ -267,28 +284,28 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(vmalloc)
\
checker((expected_pages) * PAGE_SIZE, \
kvmalloc((alloc_pages) * PAGE_SIZE, gfp), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvmalloc_node((alloc_pages) * PAGE_SIZE, gfp, NUMA_NO_NODE), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvzalloc((alloc_pages) * PAGE_SIZE, gfp), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvzalloc_node((alloc_pages) * PAGE_SIZE, gfp, NUMA_NO_NODE), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvcalloc(1, (alloc_pages) * PAGE_SIZE, gfp), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvcalloc((alloc_pages) * PAGE_SIZE, 1, gfp), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvmalloc_array(1, (alloc_pages) * PAGE_SIZE, gfp), \
vfree(p)); \
kvfree(p)); \
checker((expected_pages) * PAGE_SIZE, \
kvmalloc_array((alloc_pages) * PAGE_SIZE, 1, gfp), \
vfree(p)); \
kvfree(p)); \
\
prev_size = (expected_pages) * PAGE_SIZE; \
orig = kvmalloc(prev_size, gfp); \
@@ -346,6 +363,31 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
} while (0)
DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc)
static const char * const test_strs[] = {
"",
"Hello there",
"A longer string, just for variety",
};
#define TEST_realloc(checker) do { \
gfp_t gfp = GFP_KERNEL; \
size_t len; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(test_strs); i++) { \
len = strlen(test_strs[i]); \
KUNIT_EXPECT_EQ(test, __builtin_constant_p(len), 0); \
checker(len, kmemdup_array(test_strs[i], len, 1, gfp), \
kfree(p)); \
checker(len, kmemdup(test_strs[i], len, gfp), \
kfree(p)); \
} \
} while (0)
static void fortify_test_realloc_size(struct kunit *test)
{
TEST_realloc(check_dynamic);
}
/*
* We can't have an array at the end of a structure or else
* builds without -fstrict-flex-arrays=3 will report them as
@@ -361,7 +403,7 @@ struct fortify_padding {
/* Force compiler into not being able to resolve size at compile-time. */
static volatile int unconst;
static void strlen_test(struct kunit *test)
static void fortify_test_strlen(struct kunit *test)
{
struct fortify_padding pad = { };
int i, end = sizeof(pad.buf) - 1;
@@ -384,7 +426,7 @@ static void strlen_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
}
static void strnlen_test(struct kunit *test)
static void fortify_test_strnlen(struct kunit *test)
{
struct fortify_padding pad = { };
int i, end = sizeof(pad.buf) - 1;
@@ -422,7 +464,7 @@ static void strnlen_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void strcpy_test(struct kunit *test)
static void fortify_test_strcpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf) + 1] = { };
@@ -480,7 +522,7 @@ static void strcpy_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strncpy_test(struct kunit *test)
static void fortify_test_strncpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
@@ -539,7 +581,7 @@ static void strncpy_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strscpy_test(struct kunit *test)
static void fortify_test_strscpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
@@ -596,7 +638,7 @@ static void strscpy_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strcat_test(struct kunit *test)
static void fortify_test_strcat(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf) / 2] = { };
@@ -653,7 +695,7 @@ static void strcat_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strncat_test(struct kunit *test)
static void fortify_test_strncat(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf)] = { };
@@ -726,7 +768,7 @@ static void strncat_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void strlcat_test(struct kunit *test)
static void fortify_test_strlcat(struct kunit *test)
{
struct fortify_padding pad = { };
char src[sizeof(pad.buf)] = { };
@@ -811,7 +853,75 @@ static void strlcat_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void memscan_test(struct kunit *test)
/* Check for 0-sized arrays... */
struct fortify_zero_sized {
unsigned long bytes_before;
char buf[0];
unsigned long bytes_after;
};
#define __fortify_test(memfunc) \
static void fortify_test_##memfunc(struct kunit *test) \
{ \
struct fortify_zero_sized zero = { }; \
struct fortify_padding pad = { }; \
char srcA[sizeof(pad.buf) + 2]; \
char srcB[sizeof(pad.buf) + 2]; \
size_t len = sizeof(pad.buf) + unconst; \
\
memset(srcA, 'A', sizeof(srcA)); \
KUNIT_ASSERT_EQ(test, srcA[0], 'A'); \
memset(srcB, 'B', sizeof(srcB)); \
KUNIT_ASSERT_EQ(test, srcB[0], 'B'); \
\
memfunc(pad.buf, srcA, 0 + unconst); \
KUNIT_EXPECT_EQ(test, pad.buf[0], '\0'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
memfunc(pad.buf + 1, srcB, 1 + unconst); \
KUNIT_EXPECT_EQ(test, pad.buf[0], '\0'); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'B'); \
KUNIT_EXPECT_EQ(test, pad.buf[2], '\0'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
memfunc(pad.buf, srcA, 1 + unconst); \
KUNIT_EXPECT_EQ(test, pad.buf[0], 'A'); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'B'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
memfunc(pad.buf, srcA, len - 1); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'A'); \
KUNIT_EXPECT_EQ(test, pad.buf[len - 1], '\0'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
memfunc(pad.buf, srcA, len); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'A'); \
KUNIT_EXPECT_EQ(test, pad.buf[len - 1], 'A'); \
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
memfunc(pad.buf, srcA, len + 1); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); \
memfunc(pad.buf + 1, srcB, len); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2); \
\
/* Reset error counter. */ \
fortify_write_overflows = 0; \
/* Copy nothing into nothing: no errors. */ \
memfunc(zero.buf, srcB, 0 + unconst); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
/* We currently explicitly ignore zero-sized dests. */ \
memfunc(zero.buf, srcB, 1 + unconst); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
}
__fortify_test(memcpy)
__fortify_test(memmove)
static void fortify_test_memscan(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
@@ -830,7 +940,7 @@ static void memscan_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memchr_test(struct kunit *test)
static void fortify_test_memchr(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
@@ -849,7 +959,7 @@ static void memchr_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memchr_inv_test(struct kunit *test)
static void fortify_test_memchr_inv(struct kunit *test)
{
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + 1;
@@ -869,7 +979,7 @@ static void memchr_inv_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void memcmp_test(struct kunit *test)
static void fortify_test_memcmp(struct kunit *test)
{
char one[] = "My mind is going ...";
char two[] = "My mind is going ... I can feel it.";
@@ -891,7 +1001,7 @@ static void memcmp_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 2);
}
static void kmemdup_test(struct kunit *test)
static void fortify_test_kmemdup(struct kunit *test)
{
char src[] = "I got Doom running on it!";
char *copy;
@@ -917,19 +1027,19 @@ static void kmemdup_test(struct kunit *test)
/* Out of bounds by 1 byte. */
copy = kmemdup(src, len + 1, GFP_KERNEL);
KUNIT_EXPECT_NULL(test, copy);
KUNIT_EXPECT_PTR_EQ(test, copy, ZERO_SIZE_PTR);
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_PTR_EQ(test, copy, ZERO_SIZE_PTR);
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_PTR_EQ(test, copy, ZERO_SIZE_PTR);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 3);
kfree(copy);
}
@@ -951,31 +1061,33 @@ static int fortify_test_init(struct kunit *test)
}
static struct kunit_case fortify_test_cases[] = {
KUNIT_CASE(known_sizes_test),
KUNIT_CASE(control_flow_split_test),
KUNIT_CASE(alloc_size_kmalloc_const_test),
KUNIT_CASE(alloc_size_kmalloc_dynamic_test),
KUNIT_CASE(alloc_size_vmalloc_const_test),
KUNIT_CASE(alloc_size_vmalloc_dynamic_test),
KUNIT_CASE(alloc_size_kvmalloc_const_test),
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),
KUNIT_CASE(fortify_test_known_sizes),
KUNIT_CASE(fortify_test_control_flow_split),
KUNIT_CASE(fortify_test_alloc_size_kmalloc_const),
KUNIT_CASE(fortify_test_alloc_size_kmalloc_dynamic),
KUNIT_CASE(fortify_test_alloc_size_vmalloc_const),
KUNIT_CASE(fortify_test_alloc_size_vmalloc_dynamic),
KUNIT_CASE(fortify_test_alloc_size_kvmalloc_const),
KUNIT_CASE(fortify_test_alloc_size_kvmalloc_dynamic),
KUNIT_CASE(fortify_test_alloc_size_devm_kmalloc_const),
KUNIT_CASE(fortify_test_alloc_size_devm_kmalloc_dynamic),
KUNIT_CASE(fortify_test_realloc_size),
KUNIT_CASE(fortify_test_strlen),
KUNIT_CASE(fortify_test_strnlen),
KUNIT_CASE(fortify_test_strcpy),
KUNIT_CASE(fortify_test_strncpy),
KUNIT_CASE(fortify_test_strscpy),
KUNIT_CASE(fortify_test_strcat),
KUNIT_CASE(fortify_test_strncat),
KUNIT_CASE(fortify_test_strlcat),
/* 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),
KUNIT_CASE(fortify_test_memcpy),
KUNIT_CASE(fortify_test_memmove),
KUNIT_CASE(fortify_test_memscan),
KUNIT_CASE(fortify_test_memchr),
KUNIT_CASE(fortify_test_memchr_inv),
KUNIT_CASE(fortify_test_memcmp),
KUNIT_CASE(fortify_test_kmemdup),
{}
};
-53
View File
@@ -493,58 +493,6 @@ static void memmove_overlap_test(struct kunit *test)
}
}
static void strtomem_test(struct kunit *test)
{
static const char input[sizeof(unsigned long)] = "hi";
static const char truncate[] = "this is too long";
struct {
unsigned long canary1;
unsigned char output[sizeof(unsigned long)] __nonstring;
unsigned long canary2;
} wrap;
memset(&wrap, 0xFF, sizeof(wrap));
KUNIT_EXPECT_EQ_MSG(test, wrap.canary1, ULONG_MAX,
"bad initial canary value");
KUNIT_EXPECT_EQ_MSG(test, wrap.canary2, ULONG_MAX,
"bad initial canary value");
/* Check unpadded copy leaves surroundings untouched. */
strtomem(wrap.output, input);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
for (size_t i = 2; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], 0xFF);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check truncated copy leaves surroundings untouched. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem(wrap.output, truncate);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
for (size_t i = 0; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check padded copy leaves only string padded. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem_pad(wrap.output, input, 0xAA);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
for (size_t i = 2; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], 0xAA);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check truncated padded copy has no padding. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem(wrap.output, truncate);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
for (size_t i = 0; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
}
static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE(memset_test),
KUNIT_CASE(memcpy_test),
@@ -552,7 +500,6 @@ static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE_SLOW(memmove_test),
KUNIT_CASE_SLOW(memmove_large_test),
KUNIT_CASE_SLOW(memmove_overlap_test),
KUNIT_CASE(strtomem_test),
{}
};
-104
View File
@@ -1,104 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Kernel module for testing 'strcat' family of functions.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <kunit/test.h>
#include <linux/string.h>
static volatile int unconst;
static void strcat_test(struct kunit *test)
{
char dest[8];
/* Destination is terminated. */
memset(dest, 0, sizeof(dest));
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy does nothing. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "") == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in, stops at %NUL. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "four\000123") == dest);
KUNIT_EXPECT_STREQ(test, dest, "four");
KUNIT_EXPECT_EQ(test, dest[5], '\0');
/* 2 more characters copied in okay. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "AB") == dest);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
}
static void strncat_test(struct kunit *test)
{
char dest[8];
/* Destination is terminated. */
memset(dest, 0, sizeof(dest));
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy of size 0 does nothing. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "", 0 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Empty copy of size 1 does nothing too. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "", 1 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Copy of max 0 characters should do nothing. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "asdf", 0 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in, even if max is 8. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "four\000123", 8 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "four");
KUNIT_EXPECT_EQ(test, dest[5], '\0');
KUNIT_EXPECT_EQ(test, dest[6], '\0');
/* 2 characters copied in okay, 2 ignored. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "ABCD", 2 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
}
static void strlcat_test(struct kunit *test)
{
char dest[8] = "";
int len = sizeof(dest) + unconst;
/* Destination is terminated. */
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy is size 0. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "", len), 0);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Size 1 should keep buffer terminated, report size of source only. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "four", 1 + unconst), 4);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "four", len), 4);
KUNIT_EXPECT_STREQ(test, dest, "four");
/* 2 characters copied in okay, gets to 6 total. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "AB", len), 6);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
/* 2 characters ignored if max size (7) reached. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "CD", 7 + unconst), 8);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
/* 1 of 2 characters skipped, now at true max size. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "EFG", len), 9);
KUNIT_EXPECT_STREQ(test, dest, "fourABE");
/* Everything else ignored, now at full size. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "1234", len), 11);
KUNIT_EXPECT_STREQ(test, dest, "fourABE");
}
static struct kunit_case strcat_test_cases[] = {
KUNIT_CASE(strcat_test),
KUNIT_CASE(strncat_test),
KUNIT_CASE(strlcat_test),
{}
};
static struct kunit_suite strcat_test_suite = {
.name = "strcat",
.test_cases = strcat_test_cases,
};
kunit_test_suite(strcat_test_suite);
MODULE_LICENSE("GPL");
+449 -12
View File
@@ -11,7 +11,13 @@
#include <linux/slab.h>
#include <linux/string.h>
static void test_memset16(struct kunit *test)
#define STRCMP_LARGE_BUF_LEN 2048
#define STRCMP_CHANGE_POINT 1337
#define STRCMP_TEST_EXPECT_EQUAL(test, fn, ...) KUNIT_EXPECT_EQ(test, fn(__VA_ARGS__), 0)
#define STRCMP_TEST_EXPECT_LOWER(test, fn, ...) KUNIT_EXPECT_LT(test, fn(__VA_ARGS__), 0)
#define STRCMP_TEST_EXPECT_GREATER(test, fn, ...) KUNIT_EXPECT_GT(test, fn(__VA_ARGS__), 0)
static void string_test_memset16(struct kunit *test)
{
unsigned i, j, k;
u16 v, *p;
@@ -40,7 +46,7 @@ static void test_memset16(struct kunit *test)
}
}
static void test_memset32(struct kunit *test)
static void string_test_memset32(struct kunit *test)
{
unsigned i, j, k;
u32 v, *p;
@@ -69,7 +75,7 @@ static void test_memset32(struct kunit *test)
}
}
static void test_memset64(struct kunit *test)
static void string_test_memset64(struct kunit *test)
{
unsigned i, j, k;
u64 v, *p;
@@ -98,7 +104,7 @@ static void test_memset64(struct kunit *test)
}
}
static void test_strchr(struct kunit *test)
static void string_test_strchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
@@ -121,7 +127,7 @@ static void test_strchr(struct kunit *test)
KUNIT_ASSERT_NULL(test, result);
}
static void test_strnchr(struct kunit *test)
static void string_test_strnchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
const char *empty_string = "";
@@ -154,7 +160,7 @@ static void test_strnchr(struct kunit *test)
KUNIT_ASSERT_NULL(test, result);
}
static void test_strspn(struct kunit *test)
static void string_test_strspn(struct kunit *test)
{
static const struct strspn_test {
const char str[16];
@@ -179,13 +185,444 @@ static void test_strspn(struct kunit *test)
}
}
static char strcmp_buffer1[STRCMP_LARGE_BUF_LEN];
static char strcmp_buffer2[STRCMP_LARGE_BUF_LEN];
static void strcmp_fill_buffers(char fill1, char fill2)
{
memset(strcmp_buffer1, fill1, STRCMP_LARGE_BUF_LEN);
memset(strcmp_buffer2, fill2, STRCMP_LARGE_BUF_LEN);
strcmp_buffer1[STRCMP_LARGE_BUF_LEN - 1] = 0;
strcmp_buffer2[STRCMP_LARGE_BUF_LEN - 1] = 0;
}
static void string_test_strcmp(struct kunit *test)
{
/* Equal strings */
STRCMP_TEST_EXPECT_EQUAL(test, strcmp, "Hello, Kernel!", "Hello, Kernel!");
/* First string is lexicographically less than the second */
STRCMP_TEST_EXPECT_LOWER(test, strcmp, "Hello, KUnit!", "Hello, Kernel!");
/* First string is lexicographically larger than the second */
STRCMP_TEST_EXPECT_GREATER(test, strcmp, "Hello, Kernel!", "Hello, KUnit!");
/* Empty string is always lexicographically less than any non-empty string */
STRCMP_TEST_EXPECT_LOWER(test, strcmp, "", "Non-empty string");
/* Two empty strings should be equal */
STRCMP_TEST_EXPECT_EQUAL(test, strcmp, "", "");
/* Compare two strings which have only one char difference */
STRCMP_TEST_EXPECT_LOWER(test, strcmp, "Abacaba", "Abadaba");
/* Compare two strings which have the same prefix*/
STRCMP_TEST_EXPECT_LOWER(test, strcmp, "Just a string", "Just a string and something else");
}
static void string_test_strcmp_long_strings(struct kunit *test)
{
strcmp_fill_buffers('B', 'B');
STRCMP_TEST_EXPECT_EQUAL(test, strcmp, strcmp_buffer1, strcmp_buffer2);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'A';
STRCMP_TEST_EXPECT_LOWER(test, strcmp, strcmp_buffer1, strcmp_buffer2);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'C';
STRCMP_TEST_EXPECT_GREATER(test, strcmp, strcmp_buffer1, strcmp_buffer2);
}
static void string_test_strncmp(struct kunit *test)
{
/* Equal strings */
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, "Hello, KUnit!", "Hello, KUnit!", 13);
/* First string is lexicographically less than the second */
STRCMP_TEST_EXPECT_LOWER(test, strncmp, "Hello, KUnit!", "Hello, Kernel!", 13);
/* Result is always 'equal' when count = 0 */
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, "Hello, Kernel!", "Hello, KUnit!", 0);
/* Strings with common prefix are equal if count = length of prefix */
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, "Abacaba", "Abadaba", 3);
/* Strings with common prefix are not equal when count = length of prefix + 1 */
STRCMP_TEST_EXPECT_LOWER(test, strncmp, "Abacaba", "Abadaba", 4);
/* If one string is a prefix of another, the shorter string is lexicographically smaller */
STRCMP_TEST_EXPECT_LOWER(test, strncmp, "Just a string", "Just a string and something else",
strlen("Just a string and something else"));
/*
* If one string is a prefix of another, and we check first length
* of prefix chars, the result is 'equal'
*/
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, "Just a string", "Just a string and something else",
strlen("Just a string"));
}
static void string_test_strncmp_long_strings(struct kunit *test)
{
strcmp_fill_buffers('B', 'B');
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'A';
STRCMP_TEST_EXPECT_LOWER(test, strncmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'C';
STRCMP_TEST_EXPECT_GREATER(test, strncmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
/* the strings are equal up to STRCMP_CHANGE_POINT */
STRCMP_TEST_EXPECT_EQUAL(test, strncmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_CHANGE_POINT);
STRCMP_TEST_EXPECT_GREATER(test, strncmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_CHANGE_POINT + 1);
}
static void string_test_strcasecmp(struct kunit *test)
{
/* Same strings in different case should be equal */
STRCMP_TEST_EXPECT_EQUAL(test, strcasecmp, "Hello, Kernel!", "HeLLO, KErNeL!");
/* Empty strings should be equal */
STRCMP_TEST_EXPECT_EQUAL(test, strcasecmp, "", "");
/* Despite ascii code for 'a' is larger than ascii code for 'B', 'a' < 'B' */
STRCMP_TEST_EXPECT_LOWER(test, strcasecmp, "a", "B");
STRCMP_TEST_EXPECT_GREATER(test, strcasecmp, "B", "a");
/* Special symbols and numbers should be processed correctly */
STRCMP_TEST_EXPECT_EQUAL(test, strcasecmp, "-+**.1230ghTTT~^", "-+**.1230Ghttt~^");
}
static void string_test_strcasecmp_long_strings(struct kunit *test)
{
strcmp_fill_buffers('b', 'B');
STRCMP_TEST_EXPECT_EQUAL(test, strcasecmp, strcmp_buffer1, strcmp_buffer2);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'a';
STRCMP_TEST_EXPECT_LOWER(test, strcasecmp, strcmp_buffer1, strcmp_buffer2);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'C';
STRCMP_TEST_EXPECT_GREATER(test, strcasecmp, strcmp_buffer1, strcmp_buffer2);
}
static void string_test_strncasecmp(struct kunit *test)
{
/* Same strings in different case should be equal */
STRCMP_TEST_EXPECT_EQUAL(test, strncasecmp, "AbAcAbA", "Abacaba", strlen("Abacaba"));
/* strncasecmp should check 'count' chars only */
STRCMP_TEST_EXPECT_EQUAL(test, strncasecmp, "AbaCaBa", "abaCaDa", 5);
STRCMP_TEST_EXPECT_LOWER(test, strncasecmp, "a", "B", 1);
STRCMP_TEST_EXPECT_GREATER(test, strncasecmp, "B", "a", 1);
/* Result is always 'equal' when count = 0 */
STRCMP_TEST_EXPECT_EQUAL(test, strncasecmp, "Abacaba", "Not abacaba", 0);
}
static void string_test_strncasecmp_long_strings(struct kunit *test)
{
strcmp_fill_buffers('b', 'B');
STRCMP_TEST_EXPECT_EQUAL(test, strncasecmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'a';
STRCMP_TEST_EXPECT_LOWER(test, strncasecmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
strcmp_buffer1[STRCMP_CHANGE_POINT] = 'C';
STRCMP_TEST_EXPECT_GREATER(test, strncasecmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_LARGE_BUF_LEN);
STRCMP_TEST_EXPECT_EQUAL(test, strncasecmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_CHANGE_POINT);
STRCMP_TEST_EXPECT_GREATER(test, strncasecmp, strcmp_buffer1,
strcmp_buffer2, STRCMP_CHANGE_POINT + 1);
}
/**
* strscpy_check() - Run a specific test case.
* @test: KUnit test context pointer
* @src: Source string, argument to strscpy_pad()
* @count: Size of destination buffer, argument to strscpy_pad()
* @expected: Expected return value from call to strscpy_pad()
* @chars: Number of characters from the src string expected to be
* written to the dst buffer.
* @terminator: 1 if there should be a terminating null byte 0 otherwise.
* @pad: Number of pad characters expected (in the tail of dst buffer).
* (@pad does not include the null terminator byte.)
*
* Calls strscpy_pad() and verifies the return value and state of the
* destination buffer after the call returns.
*/
static void strscpy_check(struct kunit *test, char *src, int count,
int expected, int chars, int terminator, int pad)
{
int nr_bytes_poison;
int max_expected;
int max_count;
int written;
char buf[6];
int index, i;
const char POISON = 'z';
KUNIT_ASSERT_TRUE_MSG(test, src != NULL,
"null source string not supported");
memset(buf, POISON, sizeof(buf));
/* Future proofing test suite, validate args */
max_count = sizeof(buf) - 2; /* Space for null and to verify overflow */
max_expected = count - 1; /* Space for the null */
KUNIT_ASSERT_LE_MSG(test, count, max_count,
"count (%d) is too big (%d) ... aborting", count, max_count);
KUNIT_EXPECT_LE_MSG(test, expected, max_expected,
"expected (%d) is bigger than can possibly be returned (%d)",
expected, max_expected);
written = strscpy_pad(buf, src, count);
KUNIT_ASSERT_EQ(test, written, expected);
if (count && written == -E2BIG) {
KUNIT_ASSERT_EQ_MSG(test, 0, strncmp(buf, src, count - 1),
"buffer state invalid for -E2BIG");
KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0',
"too big string is not null terminated correctly");
}
for (i = 0; i < chars; i++)
KUNIT_ASSERT_EQ_MSG(test, buf[i], src[i],
"buf[i]==%c != src[i]==%c", buf[i], src[i]);
if (terminator)
KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0',
"string is not null terminated correctly");
for (i = 0; i < pad; i++) {
index = chars + terminator + i;
KUNIT_ASSERT_EQ_MSG(test, buf[index], '\0',
"padding missing at index: %d", i);
}
nr_bytes_poison = sizeof(buf) - chars - terminator - pad;
for (i = 0; i < nr_bytes_poison; i++) {
index = sizeof(buf) - 1 - i; /* Check from the end back */
KUNIT_ASSERT_EQ_MSG(test, buf[index], POISON,
"poison value missing at index: %d", i);
}
}
static void string_test_strscpy(struct kunit *test)
{
char dest[8];
/*
* strscpy_check() uses a destination buffer of size 6 and needs at
* least 2 characters spare (one for null and one to check for
* overflow). This means we should only call tc() with
* strings up to a maximum of 4 characters long and 'count'
* should not exceed 4. To test with longer strings increase
* the buffer size in tc().
*/
/* strscpy_check(test, src, count, expected, chars, terminator, pad) */
strscpy_check(test, "a", 0, -E2BIG, 0, 0, 0);
strscpy_check(test, "", 0, -E2BIG, 0, 0, 0);
strscpy_check(test, "a", 1, -E2BIG, 0, 1, 0);
strscpy_check(test, "", 1, 0, 0, 1, 0);
strscpy_check(test, "ab", 2, -E2BIG, 1, 1, 0);
strscpy_check(test, "a", 2, 1, 1, 1, 0);
strscpy_check(test, "", 2, 0, 0, 1, 1);
strscpy_check(test, "abc", 3, -E2BIG, 2, 1, 0);
strscpy_check(test, "ab", 3, 2, 2, 1, 0);
strscpy_check(test, "a", 3, 1, 1, 1, 1);
strscpy_check(test, "", 3, 0, 0, 1, 2);
strscpy_check(test, "abcd", 4, -E2BIG, 3, 1, 0);
strscpy_check(test, "abc", 4, 3, 3, 1, 0);
strscpy_check(test, "ab", 4, 2, 2, 1, 1);
strscpy_check(test, "a", 4, 1, 1, 1, 2);
strscpy_check(test, "", 4, 0, 0, 1, 3);
/* Compile-time-known source strings. */
KUNIT_EXPECT_EQ(test, strscpy(dest, "", ARRAY_SIZE(dest)), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 3), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 1), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 0), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", ARRAY_SIZE(dest)), 5);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 3), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 1), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 0), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "This is too long", ARRAY_SIZE(dest)), -E2BIG);
}
static volatile int unconst;
static void string_test_strcat(struct kunit *test)
{
char dest[8];
/* Destination is terminated. */
memset(dest, 0, sizeof(dest));
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy does nothing. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "") == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in, stops at %NUL. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "four\000123") == dest);
KUNIT_EXPECT_STREQ(test, dest, "four");
KUNIT_EXPECT_EQ(test, dest[5], '\0');
/* 2 more characters copied in okay. */
KUNIT_EXPECT_TRUE(test, strcat(dest, "AB") == dest);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
}
static void string_test_strncat(struct kunit *test)
{
char dest[8];
/* Destination is terminated. */
memset(dest, 0, sizeof(dest));
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy of size 0 does nothing. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "", 0 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Empty copy of size 1 does nothing too. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "", 1 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Copy of max 0 characters should do nothing. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "asdf", 0 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in, even if max is 8. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "four\000123", 8 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "four");
KUNIT_EXPECT_EQ(test, dest[5], '\0');
KUNIT_EXPECT_EQ(test, dest[6], '\0');
/* 2 characters copied in okay, 2 ignored. */
KUNIT_EXPECT_TRUE(test, strncat(dest, "ABCD", 2 + unconst) == dest);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
}
static void string_test_strlcat(struct kunit *test)
{
char dest[8] = "";
int len = sizeof(dest) + unconst;
/* Destination is terminated. */
KUNIT_EXPECT_EQ(test, strlen(dest), 0);
/* Empty copy is size 0. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "", len), 0);
KUNIT_EXPECT_STREQ(test, dest, "");
/* Size 1 should keep buffer terminated, report size of source only. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "four", 1 + unconst), 4);
KUNIT_EXPECT_STREQ(test, dest, "");
/* 4 characters copied in. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "four", len), 4);
KUNIT_EXPECT_STREQ(test, dest, "four");
/* 2 characters copied in okay, gets to 6 total. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "AB", len), 6);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
/* 2 characters ignored if max size (7) reached. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "CD", 7 + unconst), 8);
KUNIT_EXPECT_STREQ(test, dest, "fourAB");
/* 1 of 2 characters skipped, now at true max size. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "EFG", len), 9);
KUNIT_EXPECT_STREQ(test, dest, "fourABE");
/* Everything else ignored, now at full size. */
KUNIT_EXPECT_EQ(test, strlcat(dest, "1234", len), 11);
KUNIT_EXPECT_STREQ(test, dest, "fourABE");
}
static void string_test_strtomem(struct kunit *test)
{
static const char input[sizeof(unsigned long)] = "hi";
static const char truncate[] = "this is too long";
struct {
unsigned long canary1;
unsigned char output[sizeof(unsigned long)] __nonstring;
unsigned long canary2;
} wrap;
memset(&wrap, 0xFF, sizeof(wrap));
KUNIT_EXPECT_EQ_MSG(test, wrap.canary1, ULONG_MAX,
"bad initial canary value");
KUNIT_EXPECT_EQ_MSG(test, wrap.canary2, ULONG_MAX,
"bad initial canary value");
/* Check unpadded copy leaves surroundings untouched. */
strtomem(wrap.output, input);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
for (size_t i = 2; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], 0xFF);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check truncated copy leaves surroundings untouched. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem(wrap.output, truncate);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
for (size_t i = 0; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check padded copy leaves only string padded. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem_pad(wrap.output, input, 0xAA);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
KUNIT_EXPECT_EQ(test, wrap.output[0], input[0]);
KUNIT_EXPECT_EQ(test, wrap.output[1], input[1]);
for (size_t i = 2; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], 0xAA);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
/* Check truncated padded copy has no padding. */
memset(&wrap, 0xFF, sizeof(wrap));
strtomem(wrap.output, truncate);
KUNIT_EXPECT_EQ(test, wrap.canary1, ULONG_MAX);
for (size_t i = 0; i < sizeof(wrap.output); i++)
KUNIT_EXPECT_EQ(test, wrap.output[i], truncate[i]);
KUNIT_EXPECT_EQ(test, wrap.canary2, ULONG_MAX);
}
static void string_test_memtostr(struct kunit *test)
{
char nonstring[7] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
char nonstring_small[3] = { 'a', 'b', 'c' };
char dest[sizeof(nonstring) + 1];
/* Copy in a non-NUL-terminated string into exactly right-sized dest. */
KUNIT_EXPECT_EQ(test, sizeof(dest), sizeof(nonstring) + 1);
memset(dest, 'X', sizeof(dest));
memtostr(dest, nonstring);
KUNIT_EXPECT_STREQ(test, dest, "abcdefg");
memset(dest, 'X', sizeof(dest));
memtostr(dest, nonstring_small);
KUNIT_EXPECT_STREQ(test, dest, "abc");
KUNIT_EXPECT_EQ(test, dest[7], 'X');
memset(dest, 'X', sizeof(dest));
memtostr_pad(dest, nonstring);
KUNIT_EXPECT_STREQ(test, dest, "abcdefg");
memset(dest, 'X', sizeof(dest));
memtostr_pad(dest, nonstring_small);
KUNIT_EXPECT_STREQ(test, dest, "abc");
KUNIT_EXPECT_EQ(test, dest[7], '\0');
}
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),
KUNIT_CASE(string_test_memset16),
KUNIT_CASE(string_test_memset32),
KUNIT_CASE(string_test_memset64),
KUNIT_CASE(string_test_strchr),
KUNIT_CASE(string_test_strnchr),
KUNIT_CASE(string_test_strspn),
KUNIT_CASE(string_test_strcmp),
KUNIT_CASE(string_test_strcmp_long_strings),
KUNIT_CASE(string_test_strncmp),
KUNIT_CASE(string_test_strncmp_long_strings),
KUNIT_CASE(string_test_strcasecmp),
KUNIT_CASE(string_test_strcasecmp_long_strings),
KUNIT_CASE(string_test_strncasecmp),
KUNIT_CASE(string_test_strncasecmp_long_strings),
KUNIT_CASE(string_test_strscpy),
KUNIT_CASE(string_test_strcat),
KUNIT_CASE(string_test_strncat),
KUNIT_CASE(string_test_strlcat),
KUNIT_CASE(string_test_strtomem),
KUNIT_CASE(string_test_memtostr),
{}
};
-142
View File
@@ -1,142 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Kernel module for testing 'strscpy' family of functions.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <kunit/test.h>
#include <linux/string.h>
/*
* tc() - Run a specific test case.
* @src: Source string, argument to strscpy_pad()
* @count: Size of destination buffer, argument to strscpy_pad()
* @expected: Expected return value from call to strscpy_pad()
* @terminator: 1 if there should be a terminating null byte 0 otherwise.
* @chars: Number of characters from the src string expected to be
* written to the dst buffer.
* @pad: Number of pad characters expected (in the tail of dst buffer).
* (@pad does not include the null terminator byte.)
*
* Calls strscpy_pad() and verifies the return value and state of the
* destination buffer after the call returns.
*/
static void tc(struct kunit *test, char *src, int count, int expected,
int chars, int terminator, int pad)
{
int nr_bytes_poison;
int max_expected;
int max_count;
int written;
char buf[6];
int index, i;
const char POISON = 'z';
KUNIT_ASSERT_TRUE_MSG(test, src != NULL,
"null source string not supported");
memset(buf, POISON, sizeof(buf));
/* Future proofing test suite, validate args */
max_count = sizeof(buf) - 2; /* Space for null and to verify overflow */
max_expected = count - 1; /* Space for the null */
KUNIT_ASSERT_LE_MSG(test, count, max_count,
"count (%d) is too big (%d) ... aborting", count, max_count);
KUNIT_EXPECT_LE_MSG(test, expected, max_expected,
"expected (%d) is bigger than can possibly be returned (%d)",
expected, max_expected);
written = strscpy_pad(buf, src, count);
KUNIT_ASSERT_EQ(test, written, expected);
if (count && written == -E2BIG) {
KUNIT_ASSERT_EQ_MSG(test, 0, strncmp(buf, src, count - 1),
"buffer state invalid for -E2BIG");
KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0',
"too big string is not null terminated correctly");
}
for (i = 0; i < chars; i++)
KUNIT_ASSERT_EQ_MSG(test, buf[i], src[i],
"buf[i]==%c != src[i]==%c", buf[i], src[i]);
if (terminator)
KUNIT_ASSERT_EQ_MSG(test, buf[count - 1], '\0',
"string is not null terminated correctly");
for (i = 0; i < pad; i++) {
index = chars + terminator + i;
KUNIT_ASSERT_EQ_MSG(test, buf[index], '\0',
"padding missing at index: %d", i);
}
nr_bytes_poison = sizeof(buf) - chars - terminator - pad;
for (i = 0; i < nr_bytes_poison; i++) {
index = sizeof(buf) - 1 - i; /* Check from the end back */
KUNIT_ASSERT_EQ_MSG(test, buf[index], POISON,
"poison value missing at index: %d", i);
}
}
static void strscpy_test(struct kunit *test)
{
char dest[8];
/*
* tc() uses a destination buffer of size 6 and needs at
* least 2 characters spare (one for null and one to check for
* overflow). This means we should only call tc() with
* strings up to a maximum of 4 characters long and 'count'
* should not exceed 4. To test with longer strings increase
* the buffer size in tc().
*/
/* tc(test, src, count, expected, chars, terminator, pad) */
tc(test, "a", 0, -E2BIG, 0, 0, 0);
tc(test, "", 0, -E2BIG, 0, 0, 0);
tc(test, "a", 1, -E2BIG, 0, 1, 0);
tc(test, "", 1, 0, 0, 1, 0);
tc(test, "ab", 2, -E2BIG, 1, 1, 0);
tc(test, "a", 2, 1, 1, 1, 0);
tc(test, "", 2, 0, 0, 1, 1);
tc(test, "abc", 3, -E2BIG, 2, 1, 0);
tc(test, "ab", 3, 2, 2, 1, 0);
tc(test, "a", 3, 1, 1, 1, 1);
tc(test, "", 3, 0, 0, 1, 2);
tc(test, "abcd", 4, -E2BIG, 3, 1, 0);
tc(test, "abc", 4, 3, 3, 1, 0);
tc(test, "ab", 4, 2, 2, 1, 1);
tc(test, "a", 4, 1, 1, 1, 2);
tc(test, "", 4, 0, 0, 1, 3);
/* Compile-time-known source strings. */
KUNIT_EXPECT_EQ(test, strscpy(dest, "", ARRAY_SIZE(dest)), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 3), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 1), 0);
KUNIT_EXPECT_EQ(test, strscpy(dest, "", 0), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", ARRAY_SIZE(dest)), 5);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 3), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 1), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "Fixed", 0), -E2BIG);
KUNIT_EXPECT_EQ(test, strscpy(dest, "This is too long", ARRAY_SIZE(dest)), -E2BIG);
}
static struct kunit_case strscpy_test_cases[] = {
KUNIT_CASE(strscpy_test),
{}
};
static struct kunit_suite strscpy_test_suite = {
.name = "strscpy",
.test_cases = strscpy_test_cases,
};
kunit_test_suite(strscpy_test_suite);
MODULE_AUTHOR("Tobin C. Harding <tobin@kernel.org>");
MODULE_LICENSE("GPL");
+28 -15
View File
@@ -43,7 +43,7 @@ enum {
struct type_descriptor {
u16 type_kind;
u16 type_info;
char type_name[1];
char type_name[];
};
struct source_location {
@@ -124,19 +124,32 @@ 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);
void __ubsan_handle_out_of_bounds(void *_data, void *index);
void __ubsan_handle_shift_out_of_bounds(void *_data, void *lhs, void *rhs);
void __ubsan_handle_builtin_unreachable(void *_data);
void __ubsan_handle_load_invalid_value(void *_data, void *val);
void __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr,
unsigned long align,
unsigned long offset);
/*
* When generating Runtime Calls, Clang doesn't respect the -mregparm=3
* option used on i386: https://github.com/llvm/llvm-project/issues/89670
* Fix this for earlier Clang versions by forcing the calling convention
* to use non-register arguments.
*/
#if defined(CONFIG_X86_32) && \
defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION < 190000
# define ubsan_linkage asmlinkage
#else
# define ubsan_linkage
#endif
void ubsan_linkage __ubsan_handle_add_overflow(void *data, void *lhs, void *rhs);
void ubsan_linkage __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs);
void ubsan_linkage __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);
void ubsan_linkage __ubsan_handle_negate_overflow(void *_data, void *old_val);
void ubsan_linkage __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs);
void ubsan_linkage __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void *ptr);
void ubsan_linkage __ubsan_handle_type_mismatch_v1(void *_data, void *ptr);
void ubsan_linkage __ubsan_handle_out_of_bounds(void *_data, void *index);
void ubsan_linkage __ubsan_handle_shift_out_of_bounds(void *_data, void *lhs, void *rhs);
void ubsan_linkage __ubsan_handle_builtin_unreachable(void *_data);
void ubsan_linkage __ubsan_handle_load_invalid_value(void *_data, void *val);
void ubsan_linkage __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr,
unsigned long align,
unsigned long offset);
#endif