mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 09:13:38 +00:00
linux_kselftest-kunit-6.11-rc1
This KUnit next update for Linux 6.11-rc1 consists of: -- adds vm_mmap() allocation resource manager -- converts usercopy kselftest to KUnit -- disables usercopy testing on !CONFIG_MMU -- adds MODULE_DESCRIPTION() to core, list, and usercopy tests -- adds tests for assertion formatting functions - assert.c -- introduces KUNIT_ASSERT_MEMEQ and KUNIT_ASSERT_MEMNEQ macros -- fixes KUNIT_ASSERT_STRNEQ comments to make it clear that it is an assertion -- renames KUNIT_ASSERT_FAILURE to KUNIT_FAIL_AND_ABORT -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmaWpCYACgkQCwJExA0N QxwdPQ/9G26Q+xhbieosvXHu/04ZWTcuUP/cFRv56jLH9bKm25YbW8WZzKM/imE5 So35IT6SIYlwxn9fYyriPz372h3ZC522cu8tIVrUh5Uo3O5LbzQqdrxos9a+RuCg u6lenSksAjJRZ3S3IKDJ1ErxLnPYKyjjZFwDmV1+0Xxy30SwzFEbQqj9lY2Q4iGs KWBm0lrFPipbHdBqZcPB/mxIDyF6rhe+oeuOPU8uag6ncNN31xMpDanU8O6XEAz9 QoAiDICANbVKTRKG5xXgmsJtyLF8GON4e49kEYtCLdnESPc39hQtf3cTHeYI22HC 7OWhhOySifNIukFj1hVtxnN3ZfjtBGmbCwe5rXZFvMovE3YwAplKK61GoOaI9UV0 qPk5GGrAb/xEh2HZ9tgf8+CsqmnPQLGnVt2h3u3c28u4YzbkinqVj20KYsye39zz KzJsO2yDJH4LlIJjc8XWof1cyyo0TIJQVOwJqAieOPePnfs4zabmVOus8y1Cj07V iAvQTPPoZ165zA1cl0iSMolKkXeAgf2FjlEGbODrktKKX6Ag/PKVp3e6PW28zJbp 0p1V1IDQQAlEhbcRAZb+5y1voh+hcy++KyPwpj7lAVkmHd7RoK/mDL3W+oLdOTrB aXWs4JOlkmtUaz3EpAQZuvhYWVW7DexR9rU1SF44UAVzSdZSndw= =nnFR -----END PGP SIGNATURE----- Merge tag 'linux_kselftest-kunit-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest Pull KUnit updates from Shuah Khan: - add vm_mmap() allocation resource manager - convert usercopy kselftest to KUnit - disable usercopy testing on !CONFIG_MMU - add MODULE_DESCRIPTION() to core, list, and usercopy tests - add tests for assertion formatting functions - assert.c - introduce KUNIT_ASSERT_MEMEQ and KUNIT_ASSERT_MEMNEQ macros - fix KUNIT_ASSERT_STRNEQ comments to make it clear that it is an assertion - rename KUNIT_ASSERT_FAILURE to KUNIT_FAIL_AND_ABORT * tag 'linux_kselftest-kunit-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: kunit: Introduce KUNIT_ASSERT_MEMEQ and KUNIT_ASSERT_MEMNEQ macros kunit: Rename KUNIT_ASSERT_FAILURE to KUNIT_FAIL_AND_ABORT for readability kunit: Fix the comment of KUNIT_ASSERT_STRNEQ as assertion kunit: executor: Simplify string allocation handling kunit/usercopy: Add missing MODULE_DESCRIPTION() kunit/usercopy: Disable testing on !CONFIG_MMU usercopy: Convert test_user_copy to KUnit test kunit: test: Add vm_mmap() allocation resource manager list: test: add the missing MODULE_DESCRIPTION() macro kunit: add missing MODULE_DESCRIPTION() macros to core modules list: test: remove unused struct 'klist_test_struct' kunit: Cover 'assert.c' with tests
This commit is contained in:
commit
f8d22a3195
@ -12034,6 +12034,7 @@ F: arch/*/configs/hardening.config
|
||||
F: include/linux/overflow.h
|
||||
F: include/linux/randomize_kstack.h
|
||||
F: kernel/configs/hardening.config
|
||||
F: lib/usercopy_kunit.c
|
||||
F: mm/usercopy.c
|
||||
K: \b(add|choose)_random_kstack_offset\b
|
||||
K: \b__check_(object_size|heap_object)\b
|
||||
|
@ -31,7 +31,7 @@ static int input_test_init(struct kunit *test)
|
||||
ret = input_register_device(input_dev);
|
||||
if (ret) {
|
||||
input_free_device(input_dev);
|
||||
KUNIT_ASSERT_FAILURE(test, "Register device failed: %d", ret);
|
||||
KUNIT_FAIL_AND_ABORT(test, "Register device failed: %d", ret);
|
||||
}
|
||||
|
||||
test->priv = input_dev;
|
||||
|
@ -60,7 +60,7 @@ void kunit_assert_prologue(const struct kunit_loc *loc,
|
||||
* struct kunit_fail_assert - Represents a plain fail expectation/assertion.
|
||||
* @assert: The parent of this type.
|
||||
*
|
||||
* Represents a simple KUNIT_FAIL/KUNIT_ASSERT_FAILURE that always fails.
|
||||
* Represents a simple KUNIT_FAIL/KUNIT_FAIL_AND_ABORT that always fails.
|
||||
*/
|
||||
struct kunit_fail_assert {
|
||||
struct kunit_assert assert;
|
||||
@ -218,4 +218,15 @@ void kunit_mem_assert_format(const struct kunit_assert *assert,
|
||||
const struct va_format *message,
|
||||
struct string_stream *stream);
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
void kunit_assert_print_msg(const struct va_format *message,
|
||||
struct string_stream *stream);
|
||||
bool is_literal(const char *text, long long value);
|
||||
bool is_str_literal(const char *text, const char *value);
|
||||
void kunit_assert_hexdump(struct string_stream *stream,
|
||||
const void *buf,
|
||||
const void *compared_buf,
|
||||
const size_t len);
|
||||
#endif
|
||||
|
||||
#endif /* _KUNIT_ASSERT_H */
|
||||
|
@ -480,6 +480,23 @@ static inline void *kunit_kcalloc(struct kunit *test, size_t n, size_t size, gfp
|
||||
return kunit_kmalloc_array(test, n, size, gfp | __GFP_ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* kunit_vm_mmap() - Allocate KUnit-tracked vm_mmap() area
|
||||
* @test: The test context object.
|
||||
* @file: struct file pointer to map from, if any
|
||||
* @addr: desired address, if any
|
||||
* @len: how many bytes to allocate
|
||||
* @prot: mmap PROT_* bits
|
||||
* @flag: mmap flags
|
||||
* @offset: offset into @file to start mapping from.
|
||||
*
|
||||
* See vm_mmap() for more information.
|
||||
*/
|
||||
unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
|
||||
unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flag,
|
||||
unsigned long offset);
|
||||
|
||||
void kunit_cleanup(struct kunit *test);
|
||||
|
||||
void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...);
|
||||
@ -1211,7 +1228,18 @@ do { \
|
||||
fmt, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define KUNIT_ASSERT_FAILURE(test, fmt, ...) \
|
||||
/**
|
||||
* KUNIT_FAIL_AND_ABORT() - Always causes a test to fail and abort when evaluated.
|
||||
* @test: The test context object.
|
||||
* @fmt: an informational message to be printed when the assertion is made.
|
||||
* @...: string format arguments.
|
||||
*
|
||||
* The opposite of KUNIT_SUCCEED(), it is an assertion that always fails. In
|
||||
* other words, it always results in a failed assertion, and consequently
|
||||
* always causes the test case to fail and abort when evaluated.
|
||||
* See KUNIT_ASSERT_TRUE() for more information.
|
||||
*/
|
||||
#define KUNIT_FAIL_AND_ABORT(test, fmt, ...) \
|
||||
KUNIT_FAIL_ASSERTION(test, KUNIT_ASSERTION, fmt, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
@ -1438,12 +1466,12 @@ do { \
|
||||
##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* KUNIT_ASSERT_STRNEQ() - Expects that strings @left and @right are not equal.
|
||||
* KUNIT_ASSERT_STRNEQ() - An assertion that strings @left and @right are not equal.
|
||||
* @test: The test context object.
|
||||
* @left: an arbitrary expression that evaluates to a null terminated string.
|
||||
* @right: an arbitrary expression that evaluates to a null terminated string.
|
||||
*
|
||||
* Sets an expectation that the values that @left and @right evaluate to are
|
||||
* Sets an assertion that the values that @left and @right evaluate to are
|
||||
* not equal. This is semantically equivalent to
|
||||
* KUNIT_ASSERT_TRUE(@test, strcmp((@left), (@right))). See KUNIT_ASSERT_TRUE()
|
||||
* for more information.
|
||||
@ -1458,6 +1486,60 @@ do { \
|
||||
fmt, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* KUNIT_ASSERT_MEMEQ() - Asserts that the first @size bytes of @left and @right are equal.
|
||||
* @test: The test context object.
|
||||
* @left: An arbitrary expression that evaluates to the specified size.
|
||||
* @right: An arbitrary expression that evaluates to the specified size.
|
||||
* @size: Number of bytes compared.
|
||||
*
|
||||
* Sets an assertion that the values that @left and @right evaluate to are
|
||||
* equal. This is semantically equivalent to
|
||||
* KUNIT_ASSERT_TRUE(@test, !memcmp((@left), (@right), (@size))). See
|
||||
* KUNIT_ASSERT_TRUE() for more information.
|
||||
*
|
||||
* Although this assertion works for any memory block, it is not recommended
|
||||
* for comparing more structured data, such as structs. This assertion is
|
||||
* recommended for comparing, for example, data arrays.
|
||||
*/
|
||||
#define KUNIT_ASSERT_MEMEQ(test, left, right, size) \
|
||||
KUNIT_ASSERT_MEMEQ_MSG(test, left, right, size, NULL)
|
||||
|
||||
#define KUNIT_ASSERT_MEMEQ_MSG(test, left, right, size, fmt, ...) \
|
||||
KUNIT_MEM_ASSERTION(test, \
|
||||
KUNIT_ASSERTION, \
|
||||
left, ==, right, \
|
||||
size, \
|
||||
fmt, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* KUNIT_ASSERT_MEMNEQ() - Asserts that the first @size bytes of @left and @right are not equal.
|
||||
* @test: The test context object.
|
||||
* @left: An arbitrary expression that evaluates to the specified size.
|
||||
* @right: An arbitrary expression that evaluates to the specified size.
|
||||
* @size: Number of bytes compared.
|
||||
*
|
||||
* Sets an assertion that the values that @left and @right evaluate to are
|
||||
* not equal. This is semantically equivalent to
|
||||
* KUNIT_ASSERT_TRUE(@test, memcmp((@left), (@right), (@size))). See
|
||||
* KUNIT_ASSERT_TRUE() for more information.
|
||||
*
|
||||
* Although this assertion works for any memory block, it is not recommended
|
||||
* for comparing more structured data, such as structs. This assertion is
|
||||
* recommended for comparing, for example, data arrays.
|
||||
*/
|
||||
#define KUNIT_ASSERT_MEMNEQ(test, left, right, size) \
|
||||
KUNIT_ASSERT_MEMNEQ_MSG(test, left, right, size, NULL)
|
||||
|
||||
#define KUNIT_ASSERT_MEMNEQ_MSG(test, left, right, size, fmt, ...) \
|
||||
KUNIT_MEM_ASSERTION(test, \
|
||||
KUNIT_ASSERTION, \
|
||||
left, !=, right, \
|
||||
size, \
|
||||
fmt, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* KUNIT_ASSERT_NULL() - Asserts that pointers @ptr is null.
|
||||
* @test: The test context object.
|
||||
|
@ -115,6 +115,8 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/task.h>
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
/*
|
||||
* Minimum number of threads to boot the kernel
|
||||
*/
|
||||
@ -1328,6 +1330,7 @@ struct mm_struct *mm_alloc(void)
|
||||
memset(mm, 0, sizeof(*mm));
|
||||
return mm_init(mm, current, current_user_ns());
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(mm_alloc);
|
||||
|
||||
static inline void __mmput(struct mm_struct *mm)
|
||||
{
|
||||
|
@ -2505,18 +2505,6 @@ config TEST_VMALLOC
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_USER_COPY
|
||||
tristate "Test user/kernel boundary protections"
|
||||
depends on m
|
||||
help
|
||||
This builds the "test_user_copy" module that runs sanity checks
|
||||
on the copy_to/from_user infrastructure, making sure basic
|
||||
user/kernel boundary testing is working. If it fails to load,
|
||||
a regression has been detected in the user/kernel memory boundary
|
||||
protections.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config TEST_BPF
|
||||
tristate "Test BPF filter functionality"
|
||||
depends on m && NET
|
||||
@ -2814,6 +2802,15 @@ config SIPHASH_KUNIT_TEST
|
||||
This is intended to help people writing architecture-specific
|
||||
optimized versions. If unsure, say N.
|
||||
|
||||
config USERCOPY_KUNIT_TEST
|
||||
tristate "KUnit Test for user/kernel boundary protections"
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds the "usercopy_kunit" module that runs sanity checks
|
||||
on the copy_to/from_user infrastructure, making sure basic
|
||||
user/kernel boundary testing is working.
|
||||
|
||||
config TEST_UDELAY
|
||||
tristate "udelay test driver"
|
||||
help
|
||||
|
@ -78,7 +78,6 @@ obj-$(CONFIG_TEST_LKM) += test_module.o
|
||||
obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
|
||||
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
|
||||
obj-$(CONFIG_TEST_SORT) += test_sort.o
|
||||
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
|
||||
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
|
||||
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
|
||||
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
|
||||
@ -388,6 +387,7 @@ 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_SIPHASH_KUNIT_TEST) += siphash_kunit.o
|
||||
obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
|
||||
|
||||
|
@ -2,6 +2,7 @@ obj-$(CONFIG_KUNIT) += kunit.o
|
||||
|
||||
kunit-objs += test.o \
|
||||
resource.o \
|
||||
user_alloc.o \
|
||||
static_stub.o \
|
||||
string-stream.o \
|
||||
assert.o \
|
||||
@ -22,6 +23,7 @@ obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
|
||||
# string-stream-test compiles built-in only.
|
||||
ifeq ($(CONFIG_KUNIT_TEST),y)
|
||||
obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o
|
||||
obj-$(CONFIG_KUNIT_TEST) += assert_test.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include <kunit/assert.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "string-stream.h"
|
||||
|
||||
@ -30,8 +31,9 @@ void kunit_assert_prologue(const struct kunit_loc *loc,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_assert_prologue);
|
||||
|
||||
static void kunit_assert_print_msg(const struct va_format *message,
|
||||
struct string_stream *stream)
|
||||
VISIBLE_IF_KUNIT
|
||||
void kunit_assert_print_msg(const struct va_format *message,
|
||||
struct string_stream *stream)
|
||||
{
|
||||
if (message->fmt)
|
||||
string_stream_add(stream, "\n%pV", message);
|
||||
@ -89,7 +91,7 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
|
||||
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);
|
||||
|
||||
/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
|
||||
static bool is_literal(const char *text, long long value)
|
||||
VISIBLE_IF_KUNIT bool is_literal(const char *text, long long value)
|
||||
{
|
||||
char *buffer;
|
||||
int len;
|
||||
@ -166,7 +168,7 @@ EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);
|
||||
/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
|
||||
* Note: `text` will have ""s where as `value` will not.
|
||||
*/
|
||||
static bool is_str_literal(const char *text, const char *value)
|
||||
VISIBLE_IF_KUNIT bool is_str_literal(const char *text, const char *value)
|
||||
{
|
||||
int len;
|
||||
|
||||
@ -208,10 +210,11 @@ EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
|
||||
/* Adds a hexdump of a buffer to a string_stream comparing it with
|
||||
* a second buffer. The different bytes are marked with <>.
|
||||
*/
|
||||
static void kunit_assert_hexdump(struct string_stream *stream,
|
||||
const void *buf,
|
||||
const void *compared_buf,
|
||||
const size_t len)
|
||||
VISIBLE_IF_KUNIT
|
||||
void kunit_assert_hexdump(struct string_stream *stream,
|
||||
const void *buf,
|
||||
const void *compared_buf,
|
||||
const size_t len)
|
||||
{
|
||||
size_t i;
|
||||
const u8 *buf1 = buf;
|
||||
|
388
lib/kunit/assert_test.c
Normal file
388
lib/kunit/assert_test.c
Normal file
@ -0,0 +1,388 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* KUnit test for the assertion formatting functions.
|
||||
* Author: Ivan Orlov <ivan.orlov0322@gmail.com>
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include "string-stream.h"
|
||||
|
||||
#define TEST_PTR_EXPECTED_BUF_SIZE 32
|
||||
#define HEXDUMP_TEST_BUF_LEN 5
|
||||
#define ASSERT_TEST_EXPECT_CONTAIN(test, str, substr) KUNIT_EXPECT_TRUE(test, strstr(str, substr))
|
||||
#define ASSERT_TEST_EXPECT_NCONTAIN(test, str, substr) KUNIT_EXPECT_FALSE(test, strstr(str, substr))
|
||||
|
||||
static void kunit_test_is_literal(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("5", 5));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("0", 0));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("1234567890", 1234567890));
|
||||
KUNIT_EXPECT_TRUE(test, is_literal("-1234567890", -1234567890));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("05", 5));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("", 0));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("-0", 0));
|
||||
KUNIT_EXPECT_FALSE(test, is_literal("12#45", 1245));
|
||||
}
|
||||
|
||||
static void kunit_test_is_str_literal(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"Hello, World!\"", "Hello, World!"));
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"", ""));
|
||||
KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"\"", "\""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("", ""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"", "\""));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba", "Abacaba"));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("Abacaba\"", "Abacaba"));
|
||||
KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba\"", "\"Abacaba\""));
|
||||
}
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
|
||||
|
||||
/* this function is used to get a "char *" string from the string stream and defer its cleanup */
|
||||
static char *get_str_from_stream(struct kunit *test, struct string_stream *stream)
|
||||
{
|
||||
char *str = string_stream_get_string(stream);
|
||||
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, str);
|
||||
kunit_add_action(test, kfree_wrapper, (void *)str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void kunit_test_assert_prologue(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
char *str;
|
||||
const struct kunit_loc location = {
|
||||
.file = "testfile.c",
|
||||
.line = 1337,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* Test an expectation fail prologue */
|
||||
kunit_assert_prologue(&location, KUNIT_EXPECTATION, stream);
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "EXPECTATION");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
|
||||
|
||||
/* Test an assertion fail prologue */
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_prologue(&location, KUNIT_ASSERTION, stream);
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "ASSERTION");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
|
||||
}
|
||||
|
||||
/*
|
||||
* This function accepts an arbitrary count of parameters and generates a va_format struct,
|
||||
* which can be used to validate kunit_assert_print_msg function
|
||||
*/
|
||||
static void verify_assert_print_msg(struct kunit *test,
|
||||
struct string_stream *stream,
|
||||
char *expected, const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
const struct va_format vformat = {
|
||||
.fmt = format,
|
||||
.va = &list,
|
||||
};
|
||||
|
||||
va_start(list, format);
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_print_msg(&vformat, stream);
|
||||
KUNIT_EXPECT_STREQ(test, get_str_from_stream(test, stream), expected);
|
||||
}
|
||||
|
||||
static void kunit_test_assert_print_msg(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
verify_assert_print_msg(test, stream, "\nTest", "Test");
|
||||
verify_assert_print_msg(test, stream, "\nAbacaba -123 234", "%s %d %u",
|
||||
"Abacaba", -123, 234U);
|
||||
verify_assert_print_msg(test, stream, "", NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Further code contains the tests for different assert format functions.
|
||||
* This helper function accepts the assert format function, executes it and
|
||||
* validates the result string from the stream by checking that all of the
|
||||
* substrings exist in the output.
|
||||
*/
|
||||
static void validate_assert(assert_format_t format_func, struct kunit *test,
|
||||
const struct kunit_assert *assert,
|
||||
struct string_stream *stream, int num_checks, ...)
|
||||
{
|
||||
size_t i;
|
||||
va_list checks;
|
||||
char *cur_substr_exp;
|
||||
struct va_format message = { NULL, NULL };
|
||||
|
||||
va_start(checks, num_checks);
|
||||
string_stream_clear(stream);
|
||||
format_func(assert, &message, stream);
|
||||
|
||||
for (i = 0; i < num_checks; i++) {
|
||||
cur_substr_exp = va_arg(checks, char *);
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, get_str_from_stream(test, stream), cur_substr_exp);
|
||||
}
|
||||
}
|
||||
|
||||
static void kunit_test_unary_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_unary_assert un_assert = {
|
||||
.assert = assert,
|
||||
.condition = "expr",
|
||||
.expected_true = true,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
|
||||
stream, 2, "true", "is false");
|
||||
|
||||
un_assert.expected_true = false;
|
||||
validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
|
||||
stream, 2, "false", "is true");
|
||||
}
|
||||
|
||||
static void kunit_test_ptr_not_err_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_ptr_not_err_assert not_err_assert = {
|
||||
.assert = assert,
|
||||
.text = "expr",
|
||||
.value = NULL,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* Value is NULL. The corresponding message should be printed out */
|
||||
validate_assert(kunit_ptr_not_err_assert_format, test,
|
||||
¬_err_assert.assert,
|
||||
stream, 1, "null");
|
||||
|
||||
/* Value is not NULL, but looks like an error pointer. Error should be printed out */
|
||||
not_err_assert.value = (void *)-12;
|
||||
validate_assert(kunit_ptr_not_err_assert_format, test,
|
||||
¬_err_assert.assert, stream, 2,
|
||||
"error", "-12");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "1 + 2",
|
||||
.operation = "==",
|
||||
.right_text = "2",
|
||||
};
|
||||
const struct kunit_binary_assert binary_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = 3,
|
||||
.right_value = 2,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/*
|
||||
* Printed values should depend on the input we provide: the left text, right text, left
|
||||
* value and the right value.
|
||||
*/
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 4, "1 + 2", "2", "3", "==");
|
||||
|
||||
text.right_text = "4 - 2";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 3, "==", "1 + 2", "4 - 2");
|
||||
|
||||
text.left_text = "3";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 4, "3", "4 - 2", "2", "==");
|
||||
|
||||
text.right_text = "2";
|
||||
validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
|
||||
stream, 3, "3", "2", "==");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_ptr_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
char *addr_var_a, *addr_var_b;
|
||||
static const void *var_a = (void *)0xDEADBEEF;
|
||||
static const void *var_b = (void *)0xBADDCAFE;
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "var_a",
|
||||
.operation = "==",
|
||||
.right_text = "var_b",
|
||||
};
|
||||
struct kunit_binary_ptr_assert binary_ptr_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = var_a,
|
||||
.right_value = var_b,
|
||||
};
|
||||
|
||||
addr_var_a = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_a);
|
||||
addr_var_b = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_b);
|
||||
/*
|
||||
* Print the addresses to the buffers first.
|
||||
* This is necessary as we may have different count of leading zeros in the pointer
|
||||
* on different architectures.
|
||||
*/
|
||||
snprintf(addr_var_a, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_a);
|
||||
snprintf(addr_var_b, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_b);
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
validate_assert(kunit_binary_ptr_assert_format, test, &binary_ptr_assert.assert,
|
||||
stream, 3, addr_var_a, addr_var_b, "==");
|
||||
}
|
||||
|
||||
static void kunit_test_binary_str_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct kunit_assert assert = {};
|
||||
static const char *var_a = "abacaba";
|
||||
static const char *var_b = "kernel";
|
||||
struct kunit_binary_assert_text text = {
|
||||
.left_text = "var_a",
|
||||
.operation = "==",
|
||||
.right_text = "var_b",
|
||||
};
|
||||
struct kunit_binary_str_assert binary_str_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = var_a,
|
||||
.right_value = var_b,
|
||||
};
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
validate_assert(kunit_binary_str_assert_format, test,
|
||||
&binary_str_assert.assert,
|
||||
stream, 5, "var_a", "var_b", "\"abacaba\"",
|
||||
"\"kernel\"", "==");
|
||||
|
||||
text.left_text = "\"abacaba\"";
|
||||
validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
|
||||
stream, 4, "\"abacaba\"", "var_b", "\"kernel\"", "==");
|
||||
|
||||
text.right_text = "\"kernel\"";
|
||||
validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
|
||||
stream, 3, "\"abacaba\"", "\"kernel\"", "==");
|
||||
}
|
||||
|
||||
static const u8 hex_testbuf1[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
|
||||
0x45, 0x9d, 0x47, 0xd6, 0x47,
|
||||
0x2, 0x89, 0x8c, 0x81, 0x94,
|
||||
0x12, 0xfe, 0x01 };
|
||||
static const u8 hex_testbuf2[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
|
||||
0x45, 0x9d, 0x47, 0x21, 0x47,
|
||||
0xcd, 0x89, 0x24, 0x50, 0x94,
|
||||
0x12, 0xba, 0x01 };
|
||||
static void kunit_test_assert_hexdump(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
char *str;
|
||||
size_t i;
|
||||
char buf[HEXDUMP_TEST_BUF_LEN];
|
||||
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
/* Check that we are getting output like <xx> for non-matching numbers. */
|
||||
kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf2, sizeof(hex_testbuf1));
|
||||
str = get_str_from_stream(test, stream);
|
||||
for (i = 0; i < sizeof(hex_testbuf1); i++) {
|
||||
snprintf(buf, HEXDUMP_TEST_BUF_LEN, "<%02x>", hex_testbuf1[i]);
|
||||
if (hex_testbuf1[i] != hex_testbuf2[i])
|
||||
ASSERT_TEST_EXPECT_CONTAIN(test, str, buf);
|
||||
}
|
||||
/* We shouldn't get any <xx> numbers when comparing the buffer with itself. */
|
||||
string_stream_clear(stream);
|
||||
kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf1, sizeof(hex_testbuf1));
|
||||
str = get_str_from_stream(test, stream);
|
||||
ASSERT_TEST_EXPECT_NCONTAIN(test, str, "<");
|
||||
ASSERT_TEST_EXPECT_NCONTAIN(test, str, ">");
|
||||
}
|
||||
|
||||
static void kunit_test_mem_assert_format(struct kunit *test)
|
||||
{
|
||||
struct string_stream *stream;
|
||||
struct string_stream *expected_stream;
|
||||
struct kunit_assert assert = {};
|
||||
static const struct kunit_binary_assert_text text = {
|
||||
.left_text = "hex_testbuf1",
|
||||
.operation = "==",
|
||||
.right_text = "hex_testbuf2",
|
||||
};
|
||||
struct kunit_mem_assert mem_assert = {
|
||||
.assert = assert,
|
||||
.text = &text,
|
||||
.left_value = NULL,
|
||||
.right_value = hex_testbuf2,
|
||||
.size = sizeof(hex_testbuf1),
|
||||
};
|
||||
|
||||
expected_stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_stream);
|
||||
stream = kunit_alloc_string_stream(test, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
|
||||
/* The left value is NULL */
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 2, "hex_testbuf1", "is not null");
|
||||
|
||||
/* The right value is NULL, the left value is not NULL */
|
||||
mem_assert.left_value = hex_testbuf1;
|
||||
mem_assert.right_value = NULL;
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 2, "hex_testbuf2", "is not null");
|
||||
|
||||
/* Both arguments are not null */
|
||||
mem_assert.left_value = hex_testbuf1;
|
||||
mem_assert.right_value = hex_testbuf2;
|
||||
|
||||
validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
|
||||
stream, 3, "hex_testbuf1", "hex_testbuf2", "==");
|
||||
}
|
||||
|
||||
static struct kunit_case assert_test_cases[] = {
|
||||
KUNIT_CASE(kunit_test_is_literal),
|
||||
KUNIT_CASE(kunit_test_is_str_literal),
|
||||
KUNIT_CASE(kunit_test_assert_prologue),
|
||||
KUNIT_CASE(kunit_test_assert_print_msg),
|
||||
KUNIT_CASE(kunit_test_unary_assert_format),
|
||||
KUNIT_CASE(kunit_test_ptr_not_err_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_ptr_assert_format),
|
||||
KUNIT_CASE(kunit_test_binary_str_assert_format),
|
||||
KUNIT_CASE(kunit_test_assert_hexdump),
|
||||
KUNIT_CASE(kunit_test_mem_assert_format),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite assert_test_suite = {
|
||||
.name = "kunit-assert",
|
||||
.test_cases = assert_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&assert_test_suite);
|
@ -70,32 +70,26 @@ struct kunit_glob_filter {
|
||||
static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
|
||||
const char *filter_glob)
|
||||
{
|
||||
const int len = strlen(filter_glob);
|
||||
const char *period = strchr(filter_glob, '.');
|
||||
|
||||
if (!period) {
|
||||
parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL);
|
||||
parsed->suite_glob = kstrdup(filter_glob, GFP_KERNEL);
|
||||
if (!parsed->suite_glob)
|
||||
return -ENOMEM;
|
||||
|
||||
parsed->test_glob = NULL;
|
||||
strcpy(parsed->suite_glob, filter_glob);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
|
||||
parsed->suite_glob = kstrndup(filter_glob, period - filter_glob, GFP_KERNEL);
|
||||
if (!parsed->suite_glob)
|
||||
return -ENOMEM;
|
||||
|
||||
parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
|
||||
parsed->test_glob = kstrdup(period + 1, GFP_KERNEL);
|
||||
if (!parsed->test_glob) {
|
||||
kfree(parsed->suite_glob);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
|
||||
strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ static struct kunit_suite *alloc_fake_suite(struct kunit *test,
|
||||
|
||||
/* We normally never expect to allocate suites, hence the non-const cast. */
|
||||
suite = kunit_kzalloc(test, sizeof(*suite), GFP_KERNEL);
|
||||
strncpy((char *)suite->name, suite_name, sizeof(suite->name) - 1);
|
||||
strscpy((char *)suite->name, suite_name, sizeof(suite->name));
|
||||
suite->test_cases = test_cases;
|
||||
|
||||
return suite;
|
||||
|
@ -374,4 +374,5 @@ static struct kunit_suite example_init_test_suite = {
|
||||
*/
|
||||
kunit_test_init_section_suites(&example_init_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("Example KUnit test suite");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -871,4 +871,5 @@ kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
|
||||
&kunit_current_test_suite, &kunit_device_test_suite,
|
||||
&kunit_fault_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for core test infrastructure");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -938,4 +938,5 @@ static void __exit kunit_exit(void)
|
||||
}
|
||||
module_exit(kunit_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Base unit test (KUnit) API");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
117
lib/kunit/user_alloc.c
Normal file
117
lib/kunit/user_alloc.c
Normal file
@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KUnit userspace memory allocation resource management.
|
||||
*/
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
struct kunit_vm_mmap_resource {
|
||||
unsigned long addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* vm_mmap() arguments */
|
||||
struct kunit_vm_mmap_params {
|
||||
struct file *file;
|
||||
unsigned long addr;
|
||||
unsigned long len;
|
||||
unsigned long prot;
|
||||
unsigned long flag;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
/* Create and attach a new mm if it doesn't already exist. */
|
||||
static int kunit_attach_mm(void)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
|
||||
if (current->mm)
|
||||
return 0;
|
||||
|
||||
/* arch_pick_mmap_layout() is only sane with MMU systems. */
|
||||
if (!IS_ENABLED(CONFIG_MMU))
|
||||
return -EINVAL;
|
||||
|
||||
mm = mm_alloc();
|
||||
if (!mm)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Define the task size. */
|
||||
mm->task_size = TASK_SIZE;
|
||||
|
||||
/* Make sure we can allocate new VMAs. */
|
||||
arch_pick_mmap_layout(mm, ¤t->signal->rlim[RLIMIT_STACK]);
|
||||
|
||||
/* Attach the mm. It will be cleaned up when the process dies. */
|
||||
kthread_use_mm(mm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
|
||||
{
|
||||
struct kunit_vm_mmap_params *p = context;
|
||||
struct kunit_vm_mmap_resource vres;
|
||||
int ret;
|
||||
|
||||
ret = kunit_attach_mm();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vres.size = p->len;
|
||||
vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
|
||||
if (!vres.addr)
|
||||
return -ENOMEM;
|
||||
res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
|
||||
if (!res->data) {
|
||||
vm_munmap(vres.addr, vres.size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kunit_vm_mmap_free(struct kunit_resource *res)
|
||||
{
|
||||
struct kunit_vm_mmap_resource *vres = res->data;
|
||||
|
||||
/*
|
||||
* Since this is executed from the test monitoring process,
|
||||
* the test's mm has already been torn down. We don't need
|
||||
* to run vm_munmap(vres->addr, vres->size), only clean up
|
||||
* the vres.
|
||||
*/
|
||||
|
||||
kfree(vres);
|
||||
res->data = NULL;
|
||||
}
|
||||
|
||||
unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
|
||||
unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flag,
|
||||
unsigned long offset)
|
||||
{
|
||||
struct kunit_vm_mmap_params params = {
|
||||
.file = file,
|
||||
.addr = addr,
|
||||
.len = len,
|
||||
.prot = prot,
|
||||
.flag = flag,
|
||||
.offset = offset,
|
||||
};
|
||||
struct kunit_vm_mmap_resource *vres;
|
||||
|
||||
vres = kunit_alloc_resource(test,
|
||||
kunit_vm_mmap_init,
|
||||
kunit_vm_mmap_free,
|
||||
GFP_KERNEL,
|
||||
¶ms);
|
||||
if (vres)
|
||||
return vres->addr;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_vm_mmap);
|
||||
|
||||
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
|
@ -1201,12 +1201,6 @@ static struct kunit_suite hlist_test_module = {
|
||||
};
|
||||
|
||||
|
||||
struct klist_test_struct {
|
||||
int data;
|
||||
struct klist klist;
|
||||
struct klist_node klist_node;
|
||||
};
|
||||
|
||||
static int node_count;
|
||||
static struct klist_node *last_node;
|
||||
|
||||
@ -1499,4 +1493,5 @@ static struct kunit_suite klist_test_module = {
|
||||
|
||||
kunit_test_suites(&list_test_module, &hlist_test_module, &klist_test_module);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit test for the Kernel Linked-list structures");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,331 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Kernel module for testing copy_to/from_user infrastructure.
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved
|
||||
*
|
||||
* Authors:
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/*
|
||||
* Several 32-bit architectures support 64-bit {get,put}_user() calls.
|
||||
* As there doesn't appear to be anything that can safely determine
|
||||
* their capability at compile-time, we just have to opt-out certain archs.
|
||||
*/
|
||||
#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
|
||||
!defined(CONFIG_M68K) && \
|
||||
!defined(CONFIG_MICROBLAZE) && \
|
||||
!defined(CONFIG_NIOS2) && \
|
||||
!defined(CONFIG_PPC32) && \
|
||||
!defined(CONFIG_SUPERH))
|
||||
# define TEST_U64
|
||||
#endif
|
||||
|
||||
#define test(condition, msg, ...) \
|
||||
({ \
|
||||
int cond = (condition); \
|
||||
if (cond) \
|
||||
pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \
|
||||
cond; \
|
||||
})
|
||||
|
||||
static bool is_zeroed(void *from, size_t size)
|
||||
{
|
||||
return memchr_inv(from, 0x0, size) == NULL;
|
||||
}
|
||||
|
||||
static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t start, end, i, zero_start, zero_end;
|
||||
|
||||
if (test(size < 2 * PAGE_SIZE, "buffer too small"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We want to cross a page boundary to exercise the code more
|
||||
* effectively. We also don't want to make the size we scan too large,
|
||||
* otherwise the test can take a long time and cause soft lockups. So
|
||||
* scan a 1024 byte region across the page boundary.
|
||||
*/
|
||||
size = 1024;
|
||||
start = PAGE_SIZE - (size / 2);
|
||||
|
||||
kmem += start;
|
||||
umem += start;
|
||||
|
||||
zero_start = size / 4;
|
||||
zero_end = size - zero_start;
|
||||
|
||||
/*
|
||||
* We conduct a series of check_nonzero_user() tests on a block of
|
||||
* memory with the following byte-pattern (trying every possible
|
||||
* [start,end] pair):
|
||||
*
|
||||
* [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
|
||||
*
|
||||
* And we verify that check_nonzero_user() acts identically to
|
||||
* memchr_inv().
|
||||
*/
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
for (i = 1; i < zero_start; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
for (i = zero_end; i < size; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
|
||||
ret |= test(copy_to_user(umem, kmem, size),
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
for (start = 0; start <= size; start++) {
|
||||
for (end = start; end <= size; end++) {
|
||||
size_t len = end - start;
|
||||
int retval = check_zeroed_user(umem + start, len);
|
||||
int expected = is_zeroed(kmem + start, len);
|
||||
|
||||
ret |= test(retval != expected,
|
||||
"check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
|
||||
retval, expected, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_copy_struct_from_user(char *kmem, char __user *umem,
|
||||
size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
char *umem_src = NULL, *expected = NULL;
|
||||
size_t ksize, usize;
|
||||
|
||||
umem_src = kmalloc(size, GFP_KERNEL);
|
||||
ret = test(umem_src == NULL, "kmalloc failed");
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
expected = kmalloc(size, GFP_KERNEL);
|
||||
ret = test(expected == NULL, "kmalloc failed");
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
/* Fill umem with a fixed byte pattern. */
|
||||
memset(umem_src, 0x3e, size);
|
||||
ret |= test(copy_to_user(umem, umem_src, size),
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
/* Check basic case -- (usize == ksize). */
|
||||
ksize = size;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize == ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize == ksize) gives unexpected copy");
|
||||
|
||||
/* Old userspace case -- (usize < ksize). */
|
||||
ksize = size;
|
||||
usize = size / 2;
|
||||
|
||||
memcpy(expected, umem_src, usize);
|
||||
memset(expected + usize, 0x0, ksize - usize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize < ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize < ksize) gives unexpected copy");
|
||||
|
||||
/* New userspace (-E2BIG) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG,
|
||||
"copy_struct_from_user(usize > ksize) didn't give E2BIG");
|
||||
|
||||
/* New userspace (success) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
ret |= test(clear_user(umem + ksize, usize - ksize),
|
||||
"legitimate clear_user failed");
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
|
||||
"copy_struct_from_user(usize > ksize) failed");
|
||||
ret |= test(memcmp(kmem, expected, ksize),
|
||||
"copy_struct_from_user(usize > ksize) gives unexpected copy");
|
||||
|
||||
out_free:
|
||||
kfree(expected);
|
||||
kfree(umem_src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init test_user_copy_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
char *kmem;
|
||||
char __user *usermem;
|
||||
char *bad_usermem;
|
||||
unsigned long user_addr;
|
||||
u8 val_u8;
|
||||
u16 val_u16;
|
||||
u32 val_u32;
|
||||
#ifdef TEST_U64
|
||||
u64 val_u64;
|
||||
#endif
|
||||
|
||||
kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
|
||||
if (!kmem)
|
||||
return -ENOMEM;
|
||||
|
||||
user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
if (user_addr >= (unsigned long)(TASK_SIZE)) {
|
||||
pr_warn("Failed to allocate user memory\n");
|
||||
kfree(kmem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usermem = (char __user *)user_addr;
|
||||
bad_usermem = (char *)user_addr;
|
||||
|
||||
/*
|
||||
* Legitimate usage: none of these copies should fail.
|
||||
*/
|
||||
memset(kmem, 0x3a, PAGE_SIZE * 2);
|
||||
ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
|
||||
"legitimate copy_to_user failed");
|
||||
memset(kmem, 0x0, PAGE_SIZE);
|
||||
ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
|
||||
"legitimate copy_from_user failed");
|
||||
ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE),
|
||||
"legitimate usercopy failed to copy data");
|
||||
|
||||
#define test_legit(size, check) \
|
||||
do { \
|
||||
val_##size = check; \
|
||||
ret |= test(put_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate put_user (" #size ") failed"); \
|
||||
val_##size = 0; \
|
||||
ret |= test(get_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate get_user (" #size ") failed"); \
|
||||
ret |= test(val_##size != check, \
|
||||
"legitimate get_user (" #size ") failed to do copy"); \
|
||||
if (val_##size != check) { \
|
||||
pr_info("0x%llx != 0x%llx\n", \
|
||||
(unsigned long long)val_##size, \
|
||||
(unsigned long long)check); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
test_legit(u8, 0x5a);
|
||||
test_legit(u16, 0x5a5b);
|
||||
test_legit(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_legit(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_legit
|
||||
|
||||
/* Test usage of check_nonzero_user(). */
|
||||
ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE);
|
||||
/* Test usage of copy_struct_from_user(). */
|
||||
ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE);
|
||||
|
||||
/*
|
||||
* Invalid usage: none of these copies should succeed.
|
||||
*/
|
||||
|
||||
/* Prepare kernel memory with check values. */
|
||||
memset(kmem, 0x5a, PAGE_SIZE);
|
||||
memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
|
||||
|
||||
/* Reject kernel-to-kernel copies through copy_from_user(). */
|
||||
ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
|
||||
PAGE_SIZE),
|
||||
"illegal all-kernel copy_from_user passed");
|
||||
|
||||
/* Destination half of buffer should have been zeroed. */
|
||||
ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE),
|
||||
"zeroing failure for illegal all-kernel copy_from_user");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* When running with SMAP/PAN/etc, this will Oops the kernel
|
||||
* due to the zeroing of userspace memory on failure. This needs
|
||||
* to be tested in LKDTM instead, since this test module does not
|
||||
* expect to explode.
|
||||
*/
|
||||
ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
|
||||
PAGE_SIZE),
|
||||
"illegal reversed copy_from_user passed");
|
||||
#endif
|
||||
ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
|
||||
PAGE_SIZE),
|
||||
"illegal all-kernel copy_to_user passed");
|
||||
ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
|
||||
PAGE_SIZE),
|
||||
"illegal reversed copy_to_user passed");
|
||||
|
||||
#define test_illegal(size, check) \
|
||||
do { \
|
||||
val_##size = (check); \
|
||||
ret |= test(!get_user(val_##size, (size __user *)kmem), \
|
||||
"illegal get_user (" #size ") passed"); \
|
||||
ret |= test(val_##size != (size)0, \
|
||||
"zeroing failure for illegal get_user (" #size ")"); \
|
||||
if (val_##size != (size)0) { \
|
||||
pr_info("0x%llx != 0\n", \
|
||||
(unsigned long long)val_##size); \
|
||||
} \
|
||||
ret |= test(!put_user(val_##size, (size __user *)kmem), \
|
||||
"illegal put_user (" #size ") passed"); \
|
||||
} while (0)
|
||||
|
||||
test_illegal(u8, 0x5a);
|
||||
test_illegal(u16, 0x5a5b);
|
||||
test_illegal(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_illegal
|
||||
|
||||
vm_munmap(user_addr, PAGE_SIZE * 2);
|
||||
kfree(kmem);
|
||||
|
||||
if (ret == 0) {
|
||||
pr_info("tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
module_init(test_user_copy_init);
|
||||
|
||||
static void __exit test_user_copy_exit(void)
|
||||
{
|
||||
pr_info("unloaded.\n");
|
||||
}
|
||||
|
||||
module_exit(test_user_copy_exit);
|
||||
|
||||
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
|
||||
MODULE_LICENSE("GPL");
|
335
lib/usercopy_kunit.c
Normal file
335
lib/usercopy_kunit.c
Normal file
@ -0,0 +1,335 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Kernel module for testing copy_to/from_user infrastructure.
|
||||
*
|
||||
* Copyright 2013 Google Inc. All Rights Reserved
|
||||
*
|
||||
* Authors:
|
||||
* Kees Cook <keescook@chromium.org>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/mman.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
/*
|
||||
* Several 32-bit architectures support 64-bit {get,put}_user() calls.
|
||||
* As there doesn't appear to be anything that can safely determine
|
||||
* their capability at compile-time, we just have to opt-out certain archs.
|
||||
*/
|
||||
#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
|
||||
!defined(CONFIG_M68K) && \
|
||||
!defined(CONFIG_MICROBLAZE) && \
|
||||
!defined(CONFIG_NIOS2) && \
|
||||
!defined(CONFIG_PPC32) && \
|
||||
!defined(CONFIG_SUPERH))
|
||||
# define TEST_U64
|
||||
#endif
|
||||
|
||||
struct usercopy_test_priv {
|
||||
char *kmem;
|
||||
char __user *umem;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static bool is_zeroed(void *from, size_t size)
|
||||
{
|
||||
return memchr_inv(from, 0x0, size) == NULL;
|
||||
}
|
||||
|
||||
/* Test usage of check_nonzero_user(). */
|
||||
static void usercopy_test_check_nonzero_user(struct kunit *test)
|
||||
{
|
||||
size_t start, end, i, zero_start, zero_end;
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *umem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
size_t size = priv->size;
|
||||
|
||||
KUNIT_ASSERT_GE_MSG(test, size, 2 * PAGE_SIZE, "buffer too small");
|
||||
|
||||
/*
|
||||
* We want to cross a page boundary to exercise the code more
|
||||
* effectively. We also don't want to make the size we scan too large,
|
||||
* otherwise the test can take a long time and cause soft lockups. So
|
||||
* scan a 1024 byte region across the page boundary.
|
||||
*/
|
||||
size = 1024;
|
||||
start = PAGE_SIZE - (size / 2);
|
||||
|
||||
kmem += start;
|
||||
umem += start;
|
||||
|
||||
zero_start = size / 4;
|
||||
zero_end = size - zero_start;
|
||||
|
||||
/*
|
||||
* We conduct a series of check_nonzero_user() tests on a block of
|
||||
* memory with the following byte-pattern (trying every possible
|
||||
* [start,end] pair):
|
||||
*
|
||||
* [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
|
||||
*
|
||||
* And we verify that check_nonzero_user() acts identically to
|
||||
* memchr_inv().
|
||||
*/
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
for (i = 1; i < zero_start; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
for (i = zero_end; i < size; i += 2)
|
||||
kmem[i] = 0xff;
|
||||
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_to_user(umem, kmem, size), 0,
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
for (start = 0; start <= size; start++) {
|
||||
for (end = start; end <= size; end++) {
|
||||
size_t len = end - start;
|
||||
int retval = check_zeroed_user(umem + start, len);
|
||||
int expected = is_zeroed(kmem + start, len);
|
||||
|
||||
KUNIT_ASSERT_EQ_MSG(test, retval, expected,
|
||||
"check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
|
||||
retval, expected, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test usage of copy_struct_from_user(). */
|
||||
static void usercopy_test_copy_struct_from_user(struct kunit *test)
|
||||
{
|
||||
char *umem_src = NULL, *expected = NULL;
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *umem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
size_t size = priv->size;
|
||||
size_t ksize, usize;
|
||||
|
||||
umem_src = kunit_kmalloc(test, size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, umem_src);
|
||||
|
||||
expected = kunit_kmalloc(test, size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected);
|
||||
|
||||
/* Fill umem with a fixed byte pattern. */
|
||||
memset(umem_src, 0x3e, size);
|
||||
KUNIT_ASSERT_EQ_MSG(test, copy_to_user(umem, umem_src, size), 0,
|
||||
"legitimate copy_to_user failed");
|
||||
|
||||
/* Check basic case -- (usize == ksize). */
|
||||
ksize = size;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize == ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize == ksize) gives unexpected copy");
|
||||
|
||||
/* Old userspace case -- (usize < ksize). */
|
||||
ksize = size;
|
||||
usize = size / 2;
|
||||
|
||||
memcpy(expected, umem_src, usize);
|
||||
memset(expected + usize, 0x0, ksize - usize);
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize < ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize < ksize) gives unexpected copy");
|
||||
|
||||
/* New userspace (-E2BIG) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), -E2BIG,
|
||||
"copy_struct_from_user(usize > ksize) didn't give E2BIG");
|
||||
|
||||
/* New userspace (success) case -- (usize > ksize). */
|
||||
ksize = size / 2;
|
||||
usize = size;
|
||||
|
||||
memcpy(expected, umem_src, ksize);
|
||||
KUNIT_EXPECT_EQ_MSG(test, clear_user(umem + ksize, usize - ksize), 0,
|
||||
"legitimate clear_user failed");
|
||||
|
||||
memset(kmem, 0x0, size);
|
||||
KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
|
||||
"copy_struct_from_user(usize > ksize) failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
|
||||
"copy_struct_from_user(usize > ksize) gives unexpected copy");
|
||||
}
|
||||
|
||||
/*
|
||||
* Legitimate usage: none of these copies should fail.
|
||||
*/
|
||||
static void usercopy_test_valid(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *usermem = priv->umem;
|
||||
char *kmem = priv->kmem;
|
||||
|
||||
memset(kmem, 0x3a, PAGE_SIZE * 2);
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, copy_to_user(usermem, kmem, PAGE_SIZE),
|
||||
"legitimate copy_to_user failed");
|
||||
memset(kmem, 0x0, PAGE_SIZE);
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, copy_from_user(kmem, usermem, PAGE_SIZE),
|
||||
"legitimate copy_from_user failed");
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem, kmem + PAGE_SIZE, PAGE_SIZE,
|
||||
"legitimate usercopy failed to copy data");
|
||||
|
||||
#define test_legit(size, check) \
|
||||
do { \
|
||||
size val_##size = (check); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, \
|
||||
put_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate put_user (" #size ") failed"); \
|
||||
val_##size = 0; \
|
||||
KUNIT_EXPECT_EQ_MSG(test, 0, \
|
||||
get_user(val_##size, (size __user *)usermem), \
|
||||
"legitimate get_user (" #size ") failed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, val_##size, check, \
|
||||
"legitimate get_user (" #size ") failed to do copy"); \
|
||||
} while (0)
|
||||
|
||||
test_legit(u8, 0x5a);
|
||||
test_legit(u16, 0x5a5b);
|
||||
test_legit(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_legit(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_legit
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalid usage: none of these copies should succeed.
|
||||
*/
|
||||
static void usercopy_test_invalid(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv = test->priv;
|
||||
char __user *usermem = priv->umem;
|
||||
char *bad_usermem = (char *)usermem;
|
||||
char *kmem = priv->kmem;
|
||||
u64 *kmem_u64 = (u64 *)kmem;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ALTERNATE_USER_ADDRESS_SPACE) ||
|
||||
!IS_ENABLED(CONFIG_MMU)) {
|
||||
kunit_skip(test, "Testing for kernel/userspace address confusion is only sensible on architectures with a shared address space");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare kernel memory with check values. */
|
||||
memset(kmem, 0x5a, PAGE_SIZE);
|
||||
memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
|
||||
|
||||
/* Reject kernel-to-kernel copies through copy_from_user(). */
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
|
||||
PAGE_SIZE), 0,
|
||||
"illegal all-kernel copy_from_user passed");
|
||||
|
||||
/* Destination half of buffer should have been zeroed. */
|
||||
KUNIT_EXPECT_MEMEQ_MSG(test, kmem + PAGE_SIZE, kmem, PAGE_SIZE,
|
||||
"zeroing failure for illegal all-kernel copy_from_user");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* When running with SMAP/PAN/etc, this will Oops the kernel
|
||||
* due to the zeroing of userspace memory on failure. This needs
|
||||
* to be tested in LKDTM instead, since this test module does not
|
||||
* expect to explode.
|
||||
*/
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_from_user(bad_usermem, (char __user *)kmem,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal reversed copy_from_user passed");
|
||||
#endif
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal all-kernel copy_to_user passed");
|
||||
|
||||
KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, bad_usermem,
|
||||
PAGE_SIZE), 0,
|
||||
"illegal reversed copy_to_user passed");
|
||||
|
||||
#define test_illegal(size, check) \
|
||||
do { \
|
||||
size val_##size = (check); \
|
||||
/* get_user() */ \
|
||||
KUNIT_EXPECT_NE_MSG(test, get_user(val_##size, (size __user *)kmem), 0, \
|
||||
"illegal get_user (" #size ") passed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, val_##size, 0, \
|
||||
"zeroing failure for illegal get_user (" #size ")"); \
|
||||
/* put_user() */ \
|
||||
*kmem_u64 = 0xF09FA4AFF09FA4AF; \
|
||||
KUNIT_EXPECT_NE_MSG(test, put_user(val_##size, (size __user *)kmem), 0, \
|
||||
"illegal put_user (" #size ") passed"); \
|
||||
KUNIT_EXPECT_EQ_MSG(test, *kmem_u64, 0xF09FA4AFF09FA4AF, \
|
||||
"illegal put_user (" #size ") wrote to kernel memory!"); \
|
||||
} while (0)
|
||||
|
||||
test_illegal(u8, 0x5a);
|
||||
test_illegal(u16, 0x5a5b);
|
||||
test_illegal(u32, 0x5a5b5c5d);
|
||||
#ifdef TEST_U64
|
||||
test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
|
||||
#endif
|
||||
#undef test_illegal
|
||||
}
|
||||
|
||||
static int usercopy_test_init(struct kunit *test)
|
||||
{
|
||||
struct usercopy_test_priv *priv;
|
||||
unsigned long user_addr;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_MMU)) {
|
||||
kunit_skip(test, "Userspace allocation testing not available on non-MMU systems");
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
|
||||
test->priv = priv;
|
||||
priv->size = PAGE_SIZE * 2;
|
||||
|
||||
priv->kmem = kunit_kmalloc(test, priv->size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->kmem);
|
||||
|
||||
user_addr = kunit_vm_mmap(test, NULL, 0, priv->size,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
KUNIT_ASSERT_NE_MSG(test, user_addr, 0,
|
||||
"Could not create userspace mm");
|
||||
KUNIT_ASSERT_LT_MSG(test, user_addr, (unsigned long)TASK_SIZE,
|
||||
"Failed to allocate user memory");
|
||||
priv->umem = (char __user *)user_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kunit_case usercopy_test_cases[] = {
|
||||
KUNIT_CASE(usercopy_test_valid),
|
||||
KUNIT_CASE(usercopy_test_invalid),
|
||||
KUNIT_CASE(usercopy_test_check_nonzero_user),
|
||||
KUNIT_CASE(usercopy_test_copy_struct_from_user),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite usercopy_test_suite = {
|
||||
.name = "usercopy",
|
||||
.init = usercopy_test_init,
|
||||
.test_cases = usercopy_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&usercopy_test_suite);
|
||||
MODULE_AUTHOR("Kees Cook <kees@kernel.org>");
|
||||
MODULE_DESCRIPTION("Kernel module for testing copy_to/from_user infrastructure");
|
||||
MODULE_LICENSE("GPL");
|
@ -26,6 +26,8 @@
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "swap.h"
|
||||
|
||||
@ -482,6 +484,9 @@ void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
|
||||
clear_bit(MMF_TOPDOWN, &mm->flags);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_MMU
|
||||
EXPORT_SYMBOL_IF_KUNIT(arch_pick_mmap_layout);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __account_locked_vm - account locked pages to an mm's locked_vm
|
||||
|
Loading…
Reference in New Issue
Block a user