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:
Linus Torvalds 2024-07-16 17:42:14 -07:00
commit f8d22a3195
20 changed files with 978 additions and 373 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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,
&not_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,
&not_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);

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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, &current->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,
&params);
if (vres)
return vres->addr;
return 0;
}
EXPORT_SYMBOL_GPL(kunit_vm_mmap);
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);

View file

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

View file

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

View file

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