162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Test cases for KFENCE memory safety error detector. Since the interface with
462306a36Sopenharmony_ci * which KFENCE's reports are obtained is via the console, this is the output we
562306a36Sopenharmony_ci * should verify. For each test case checks the presence (or absence) of
662306a36Sopenharmony_ci * generated reports. Relies on 'console' tracepoint to capture reports as they
762306a36Sopenharmony_ci * appear in the kernel log.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2020, Google LLC.
1062306a36Sopenharmony_ci * Author: Alexander Potapenko <glider@google.com>
1162306a36Sopenharmony_ci *         Marco Elver <elver@google.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <kunit/test.h>
1562306a36Sopenharmony_ci#include <linux/jiffies.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/kfence.h>
1862306a36Sopenharmony_ci#include <linux/mm.h>
1962306a36Sopenharmony_ci#include <linux/random.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/spinlock.h>
2262306a36Sopenharmony_ci#include <linux/string.h>
2362306a36Sopenharmony_ci#include <linux/tracepoint.h>
2462306a36Sopenharmony_ci#include <trace/events/printk.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/kfence.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "kfence.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* May be overridden by <asm/kfence.h>. */
3162306a36Sopenharmony_ci#ifndef arch_kfence_test_address
3262306a36Sopenharmony_ci#define arch_kfence_test_address(addr) (addr)
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define KFENCE_TEST_REQUIRES(test, cond) do {			\
3662306a36Sopenharmony_ci	if (!(cond))						\
3762306a36Sopenharmony_ci		kunit_skip((test), "Test requires: " #cond);	\
3862306a36Sopenharmony_ci} while (0)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Report as observed from console. */
4162306a36Sopenharmony_cistatic struct {
4262306a36Sopenharmony_ci	spinlock_t lock;
4362306a36Sopenharmony_ci	int nlines;
4462306a36Sopenharmony_ci	char lines[2][256];
4562306a36Sopenharmony_ci} observed = {
4662306a36Sopenharmony_ci	.lock = __SPIN_LOCK_UNLOCKED(observed.lock),
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Probe for console output: obtains observed lines of interest. */
5062306a36Sopenharmony_cistatic void probe_console(void *ignore, const char *buf, size_t len)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	unsigned long flags;
5362306a36Sopenharmony_ci	int nlines;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	spin_lock_irqsave(&observed.lock, flags);
5662306a36Sopenharmony_ci	nlines = observed.nlines;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (strnstr(buf, "BUG: KFENCE: ", len) && strnstr(buf, "test_", len)) {
5962306a36Sopenharmony_ci		/*
6062306a36Sopenharmony_ci		 * KFENCE report and related to the test.
6162306a36Sopenharmony_ci		 *
6262306a36Sopenharmony_ci		 * The provided @buf is not NUL-terminated; copy no more than
6362306a36Sopenharmony_ci		 * @len bytes and let strscpy() add the missing NUL-terminator.
6462306a36Sopenharmony_ci		 */
6562306a36Sopenharmony_ci		strscpy(observed.lines[0], buf, min(len + 1, sizeof(observed.lines[0])));
6662306a36Sopenharmony_ci		nlines = 1;
6762306a36Sopenharmony_ci	} else if (nlines == 1 && (strnstr(buf, "at 0x", len) || strnstr(buf, "of 0x", len))) {
6862306a36Sopenharmony_ci		strscpy(observed.lines[nlines++], buf, min(len + 1, sizeof(observed.lines[0])));
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	WRITE_ONCE(observed.nlines, nlines); /* Publish new nlines. */
7262306a36Sopenharmony_ci	spin_unlock_irqrestore(&observed.lock, flags);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* Check if a report related to the test exists. */
7662306a36Sopenharmony_cistatic bool report_available(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return READ_ONCE(observed.nlines) == ARRAY_SIZE(observed.lines);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* Information we expect in a report. */
8262306a36Sopenharmony_cistruct expect_report {
8362306a36Sopenharmony_ci	enum kfence_error_type type; /* The type or error. */
8462306a36Sopenharmony_ci	void *fn; /* Function pointer to expected function where access occurred. */
8562306a36Sopenharmony_ci	char *addr; /* Address at which the bad access occurred. */
8662306a36Sopenharmony_ci	bool is_write; /* Is access a write. */
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const char *get_access_type(const struct expect_report *r)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return r->is_write ? "write" : "read";
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Check observed report matches information in @r. */
9562306a36Sopenharmony_cistatic bool report_matches(const struct expect_report *r)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	unsigned long addr = (unsigned long)r->addr;
9862306a36Sopenharmony_ci	bool ret = false;
9962306a36Sopenharmony_ci	unsigned long flags;
10062306a36Sopenharmony_ci	typeof(observed.lines) expect;
10162306a36Sopenharmony_ci	const char *end;
10262306a36Sopenharmony_ci	char *cur;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Doubled-checked locking. */
10562306a36Sopenharmony_ci	if (!report_available())
10662306a36Sopenharmony_ci		return false;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Generate expected report contents. */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Title */
11162306a36Sopenharmony_ci	cur = expect[0];
11262306a36Sopenharmony_ci	end = &expect[0][sizeof(expect[0]) - 1];
11362306a36Sopenharmony_ci	switch (r->type) {
11462306a36Sopenharmony_ci	case KFENCE_ERROR_OOB:
11562306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "BUG: KFENCE: out-of-bounds %s",
11662306a36Sopenharmony_ci				 get_access_type(r));
11762306a36Sopenharmony_ci		break;
11862306a36Sopenharmony_ci	case KFENCE_ERROR_UAF:
11962306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "BUG: KFENCE: use-after-free %s",
12062306a36Sopenharmony_ci				 get_access_type(r));
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case KFENCE_ERROR_CORRUPTION:
12362306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "BUG: KFENCE: memory corruption");
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	case KFENCE_ERROR_INVALID:
12662306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid %s",
12762306a36Sopenharmony_ci				 get_access_type(r));
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case KFENCE_ERROR_INVALID_FREE:
13062306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid free");
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	scnprintf(cur, end - cur, " in %pS", r->fn);
13562306a36Sopenharmony_ci	/* The exact offset won't match, remove it; also strip module name. */
13662306a36Sopenharmony_ci	cur = strchr(expect[0], '+');
13762306a36Sopenharmony_ci	if (cur)
13862306a36Sopenharmony_ci		*cur = '\0';
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Access information */
14162306a36Sopenharmony_ci	cur = expect[1];
14262306a36Sopenharmony_ci	end = &expect[1][sizeof(expect[1]) - 1];
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	switch (r->type) {
14562306a36Sopenharmony_ci	case KFENCE_ERROR_OOB:
14662306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "Out-of-bounds %s at", get_access_type(r));
14762306a36Sopenharmony_ci		addr = arch_kfence_test_address(addr);
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	case KFENCE_ERROR_UAF:
15062306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "Use-after-free %s at", get_access_type(r));
15162306a36Sopenharmony_ci		addr = arch_kfence_test_address(addr);
15262306a36Sopenharmony_ci		break;
15362306a36Sopenharmony_ci	case KFENCE_ERROR_CORRUPTION:
15462306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "Corrupted memory at");
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	case KFENCE_ERROR_INVALID:
15762306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "Invalid %s at", get_access_type(r));
15862306a36Sopenharmony_ci		addr = arch_kfence_test_address(addr);
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case KFENCE_ERROR_INVALID_FREE:
16162306a36Sopenharmony_ci		cur += scnprintf(cur, end - cur, "Invalid free of");
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	cur += scnprintf(cur, end - cur, " 0x%p", (void *)addr);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	spin_lock_irqsave(&observed.lock, flags);
16862306a36Sopenharmony_ci	if (!report_available())
16962306a36Sopenharmony_ci		goto out; /* A new report is being captured. */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Finally match expected output to what we actually observed. */
17262306a36Sopenharmony_ci	ret = strstr(observed.lines[0], expect[0]) && strstr(observed.lines[1], expect[1]);
17362306a36Sopenharmony_ciout:
17462306a36Sopenharmony_ci	spin_unlock_irqrestore(&observed.lock, flags);
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* ===== Test cases ===== */
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#define TEST_PRIV_WANT_MEMCACHE ((void *)1)
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Cache used by tests; if NULL, allocate from kmalloc instead. */
18362306a36Sopenharmony_cistatic struct kmem_cache *test_cache;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic size_t setup_test_cache(struct kunit *test, size_t size, slab_flags_t flags,
18662306a36Sopenharmony_ci			       void (*ctor)(void *))
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	if (test->priv != TEST_PRIV_WANT_MEMCACHE)
18962306a36Sopenharmony_ci		return size;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	kunit_info(test, "%s: size=%zu, ctor=%ps\n", __func__, size, ctor);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * Use SLAB_NO_MERGE to prevent merging with existing caches.
19562306a36Sopenharmony_ci	 * Use SLAB_ACCOUNT to allocate via memcg, if enabled.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	flags |= SLAB_NO_MERGE | SLAB_ACCOUNT;
19862306a36Sopenharmony_ci	test_cache = kmem_cache_create("test", size, 1, flags, ctor);
19962306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE_MSG(test, test_cache, "could not create cache");
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return size;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void test_cache_destroy(void)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	if (!test_cache)
20762306a36Sopenharmony_ci		return;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	kmem_cache_destroy(test_cache);
21062306a36Sopenharmony_ci	test_cache = NULL;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic inline size_t kmalloc_cache_alignment(size_t size)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	/* just to get ->align so no need to pass in the real caller */
21662306a36Sopenharmony_ci	enum kmalloc_cache_type type = kmalloc_type(GFP_KERNEL, 0);
21762306a36Sopenharmony_ci	return kmalloc_caches[type][__kmalloc_index(size, false)]->align;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/* Must always inline to match stack trace against caller. */
22162306a36Sopenharmony_cistatic __always_inline void test_free(void *ptr)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	if (test_cache)
22462306a36Sopenharmony_ci		kmem_cache_free(test_cache, ptr);
22562306a36Sopenharmony_ci	else
22662306a36Sopenharmony_ci		kfree(ptr);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * If this should be a KFENCE allocation, and on which side the allocation and
23162306a36Sopenharmony_ci * the closest guard page should be.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cienum allocation_policy {
23462306a36Sopenharmony_ci	ALLOCATE_ANY, /* KFENCE, any side. */
23562306a36Sopenharmony_ci	ALLOCATE_LEFT, /* KFENCE, left side of page. */
23662306a36Sopenharmony_ci	ALLOCATE_RIGHT, /* KFENCE, right side of page. */
23762306a36Sopenharmony_ci	ALLOCATE_NONE, /* No KFENCE allocation. */
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * Try to get a guarded allocation from KFENCE. Uses either kmalloc() or the
24262306a36Sopenharmony_ci * current test_cache if set up.
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_cistatic void *test_alloc(struct kunit *test, size_t size, gfp_t gfp, enum allocation_policy policy)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	void *alloc;
24762306a36Sopenharmony_ci	unsigned long timeout, resched_after;
24862306a36Sopenharmony_ci	const char *policy_name;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	switch (policy) {
25162306a36Sopenharmony_ci	case ALLOCATE_ANY:
25262306a36Sopenharmony_ci		policy_name = "any";
25362306a36Sopenharmony_ci		break;
25462306a36Sopenharmony_ci	case ALLOCATE_LEFT:
25562306a36Sopenharmony_ci		policy_name = "left";
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci	case ALLOCATE_RIGHT:
25862306a36Sopenharmony_ci		policy_name = "right";
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case ALLOCATE_NONE:
26162306a36Sopenharmony_ci		policy_name = "none";
26262306a36Sopenharmony_ci		break;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	kunit_info(test, "%s: size=%zu, gfp=%x, policy=%s, cache=%i\n", __func__, size, gfp,
26662306a36Sopenharmony_ci		   policy_name, !!test_cache);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*
26962306a36Sopenharmony_ci	 * 100x the sample interval should be more than enough to ensure we get
27062306a36Sopenharmony_ci	 * a KFENCE allocation eventually.
27162306a36Sopenharmony_ci	 */
27262306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(100 * kfence_sample_interval);
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * Especially for non-preemption kernels, ensure the allocation-gate
27562306a36Sopenharmony_ci	 * timer can catch up: after @resched_after, every failed allocation
27662306a36Sopenharmony_ci	 * attempt yields, to ensure the allocation-gate timer is scheduled.
27762306a36Sopenharmony_ci	 */
27862306a36Sopenharmony_ci	resched_after = jiffies + msecs_to_jiffies(kfence_sample_interval);
27962306a36Sopenharmony_ci	do {
28062306a36Sopenharmony_ci		if (test_cache)
28162306a36Sopenharmony_ci			alloc = kmem_cache_alloc(test_cache, gfp);
28262306a36Sopenharmony_ci		else
28362306a36Sopenharmony_ci			alloc = kmalloc(size, gfp);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		if (is_kfence_address(alloc)) {
28662306a36Sopenharmony_ci			struct slab *slab = virt_to_slab(alloc);
28762306a36Sopenharmony_ci			enum kmalloc_cache_type type = kmalloc_type(GFP_KERNEL, _RET_IP_);
28862306a36Sopenharmony_ci			struct kmem_cache *s = test_cache ?:
28962306a36Sopenharmony_ci					kmalloc_caches[type][__kmalloc_index(size, false)];
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			/*
29262306a36Sopenharmony_ci			 * Verify that various helpers return the right values
29362306a36Sopenharmony_ci			 * even for KFENCE objects; these are required so that
29462306a36Sopenharmony_ci			 * memcg accounting works correctly.
29562306a36Sopenharmony_ci			 */
29662306a36Sopenharmony_ci			KUNIT_EXPECT_EQ(test, obj_to_index(s, slab, alloc), 0U);
29762306a36Sopenharmony_ci			KUNIT_EXPECT_EQ(test, objs_per_slab(s, slab), 1);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci			if (policy == ALLOCATE_ANY)
30062306a36Sopenharmony_ci				return alloc;
30162306a36Sopenharmony_ci			if (policy == ALLOCATE_LEFT && PAGE_ALIGNED(alloc))
30262306a36Sopenharmony_ci				return alloc;
30362306a36Sopenharmony_ci			if (policy == ALLOCATE_RIGHT && !PAGE_ALIGNED(alloc))
30462306a36Sopenharmony_ci				return alloc;
30562306a36Sopenharmony_ci		} else if (policy == ALLOCATE_NONE)
30662306a36Sopenharmony_ci			return alloc;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		test_free(alloc);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		if (time_after(jiffies, resched_after))
31162306a36Sopenharmony_ci			cond_resched();
31262306a36Sopenharmony_ci	} while (time_before(jiffies, timeout));
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE_MSG(test, false, "failed to allocate from KFENCE");
31562306a36Sopenharmony_ci	return NULL; /* Unreachable. */
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void test_out_of_bounds_read(struct kunit *test)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	size_t size = 32;
32162306a36Sopenharmony_ci	struct expect_report expect = {
32262306a36Sopenharmony_ci		.type = KFENCE_ERROR_OOB,
32362306a36Sopenharmony_ci		.fn = test_out_of_bounds_read,
32462306a36Sopenharmony_ci		.is_write = false,
32562306a36Sopenharmony_ci	};
32662306a36Sopenharmony_ci	char *buf;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * If we don't have our own cache, adjust based on alignment, so that we
33262306a36Sopenharmony_ci	 * actually access guard pages on either side.
33362306a36Sopenharmony_ci	 */
33462306a36Sopenharmony_ci	if (!test_cache)
33562306a36Sopenharmony_ci		size = kmalloc_cache_alignment(size);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Test both sides. */
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT);
34062306a36Sopenharmony_ci	expect.addr = buf - 1;
34162306a36Sopenharmony_ci	READ_ONCE(*expect.addr);
34262306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
34362306a36Sopenharmony_ci	test_free(buf);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT);
34662306a36Sopenharmony_ci	expect.addr = buf + size;
34762306a36Sopenharmony_ci	READ_ONCE(*expect.addr);
34862306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
34962306a36Sopenharmony_ci	test_free(buf);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void test_out_of_bounds_write(struct kunit *test)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	size_t size = 32;
35562306a36Sopenharmony_ci	struct expect_report expect = {
35662306a36Sopenharmony_ci		.type = KFENCE_ERROR_OOB,
35762306a36Sopenharmony_ci		.fn = test_out_of_bounds_write,
35862306a36Sopenharmony_ci		.is_write = true,
35962306a36Sopenharmony_ci	};
36062306a36Sopenharmony_ci	char *buf;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
36362306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT);
36462306a36Sopenharmony_ci	expect.addr = buf - 1;
36562306a36Sopenharmony_ci	WRITE_ONCE(*expect.addr, 42);
36662306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
36762306a36Sopenharmony_ci	test_free(buf);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void test_use_after_free_read(struct kunit *test)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	const size_t size = 32;
37362306a36Sopenharmony_ci	struct expect_report expect = {
37462306a36Sopenharmony_ci		.type = KFENCE_ERROR_UAF,
37562306a36Sopenharmony_ci		.fn = test_use_after_free_read,
37662306a36Sopenharmony_ci		.is_write = false,
37762306a36Sopenharmony_ci	};
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
38062306a36Sopenharmony_ci	expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
38162306a36Sopenharmony_ci	test_free(expect.addr);
38262306a36Sopenharmony_ci	READ_ONCE(*expect.addr);
38362306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void test_double_free(struct kunit *test)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	const size_t size = 32;
38962306a36Sopenharmony_ci	struct expect_report expect = {
39062306a36Sopenharmony_ci		.type = KFENCE_ERROR_INVALID_FREE,
39162306a36Sopenharmony_ci		.fn = test_double_free,
39262306a36Sopenharmony_ci	};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
39562306a36Sopenharmony_ci	expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
39662306a36Sopenharmony_ci	test_free(expect.addr);
39762306a36Sopenharmony_ci	test_free(expect.addr); /* Double-free. */
39862306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void test_invalid_addr_free(struct kunit *test)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	const size_t size = 32;
40462306a36Sopenharmony_ci	struct expect_report expect = {
40562306a36Sopenharmony_ci		.type = KFENCE_ERROR_INVALID_FREE,
40662306a36Sopenharmony_ci		.fn = test_invalid_addr_free,
40762306a36Sopenharmony_ci	};
40862306a36Sopenharmony_ci	char *buf;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
41162306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
41262306a36Sopenharmony_ci	expect.addr = buf + 1; /* Free on invalid address. */
41362306a36Sopenharmony_ci	test_free(expect.addr); /* Invalid address free. */
41462306a36Sopenharmony_ci	test_free(buf); /* No error. */
41562306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void test_corruption(struct kunit *test)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	size_t size = 32;
42162306a36Sopenharmony_ci	struct expect_report expect = {
42262306a36Sopenharmony_ci		.type = KFENCE_ERROR_CORRUPTION,
42362306a36Sopenharmony_ci		.fn = test_corruption,
42462306a36Sopenharmony_ci	};
42562306a36Sopenharmony_ci	char *buf;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Test both sides. */
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT);
43262306a36Sopenharmony_ci	expect.addr = buf + size;
43362306a36Sopenharmony_ci	WRITE_ONCE(*expect.addr, 42);
43462306a36Sopenharmony_ci	test_free(buf);
43562306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT);
43862306a36Sopenharmony_ci	expect.addr = buf - 1;
43962306a36Sopenharmony_ci	WRITE_ONCE(*expect.addr, 42);
44062306a36Sopenharmony_ci	test_free(buf);
44162306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/*
44562306a36Sopenharmony_ci * KFENCE is unable to detect an OOB if the allocation's alignment requirements
44662306a36Sopenharmony_ci * leave a gap between the object and the guard page. Specifically, an
44762306a36Sopenharmony_ci * allocation of e.g. 73 bytes is aligned on 8 and 128 bytes for SLUB or SLAB
44862306a36Sopenharmony_ci * respectively. Therefore it is impossible for the allocated object to
44962306a36Sopenharmony_ci * contiguously line up with the right guard page.
45062306a36Sopenharmony_ci *
45162306a36Sopenharmony_ci * However, we test that an access to memory beyond the gap results in KFENCE
45262306a36Sopenharmony_ci * detecting an OOB access.
45362306a36Sopenharmony_ci */
45462306a36Sopenharmony_cistatic void test_kmalloc_aligned_oob_read(struct kunit *test)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	const size_t size = 73;
45762306a36Sopenharmony_ci	const size_t align = kmalloc_cache_alignment(size);
45862306a36Sopenharmony_ci	struct expect_report expect = {
45962306a36Sopenharmony_ci		.type = KFENCE_ERROR_OOB,
46062306a36Sopenharmony_ci		.fn = test_kmalloc_aligned_oob_read,
46162306a36Sopenharmony_ci		.is_write = false,
46262306a36Sopenharmony_ci	};
46362306a36Sopenharmony_ci	char *buf;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * The object is offset to the right, so there won't be an OOB to the
46962306a36Sopenharmony_ci	 * left of it.
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	READ_ONCE(*(buf - 1));
47262306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * @buf must be aligned on @align, therefore buf + size belongs to the
47662306a36Sopenharmony_ci	 * same page -> no OOB.
47762306a36Sopenharmony_ci	 */
47862306a36Sopenharmony_ci	READ_ONCE(*(buf + size));
47962306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* Overflowing by @align bytes will result in an OOB. */
48262306a36Sopenharmony_ci	expect.addr = buf + size + align;
48362306a36Sopenharmony_ci	READ_ONCE(*expect.addr);
48462306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	test_free(buf);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void test_kmalloc_aligned_oob_write(struct kunit *test)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	const size_t size = 73;
49262306a36Sopenharmony_ci	struct expect_report expect = {
49362306a36Sopenharmony_ci		.type = KFENCE_ERROR_CORRUPTION,
49462306a36Sopenharmony_ci		.fn = test_kmalloc_aligned_oob_write,
49562306a36Sopenharmony_ci	};
49662306a36Sopenharmony_ci	char *buf;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT);
49962306a36Sopenharmony_ci	/*
50062306a36Sopenharmony_ci	 * The object is offset to the right, so we won't get a page
50162306a36Sopenharmony_ci	 * fault immediately after it.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	expect.addr = buf + size;
50462306a36Sopenharmony_ci	WRITE_ONCE(*expect.addr, READ_ONCE(*expect.addr) + 1);
50562306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
50662306a36Sopenharmony_ci	test_free(buf);
50762306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/* Test cache shrinking and destroying with KFENCE. */
51162306a36Sopenharmony_cistatic void test_shrink_memcache(struct kunit *test)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	const size_t size = 32;
51462306a36Sopenharmony_ci	void *buf;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
51762306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, test_cache);
51862306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
51962306a36Sopenharmony_ci	kmem_cache_shrink(test_cache);
52062306a36Sopenharmony_ci	test_free(buf);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic void ctor_set_x(void *obj)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	/* Every object has at least 8 bytes. */
52862306a36Sopenharmony_ci	memset(obj, 'x', 8);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* Ensure that SL*B does not modify KFENCE objects on bulk free. */
53262306a36Sopenharmony_cistatic void test_free_bulk(struct kunit *test)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	int iter;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	for (iter = 0; iter < 5; iter++) {
53762306a36Sopenharmony_ci		const size_t size = setup_test_cache(test, get_random_u32_inclusive(8, 307),
53862306a36Sopenharmony_ci						     0, (iter & 1) ? ctor_set_x : NULL);
53962306a36Sopenharmony_ci		void *objects[] = {
54062306a36Sopenharmony_ci			test_alloc(test, size, GFP_KERNEL, ALLOCATE_RIGHT),
54162306a36Sopenharmony_ci			test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE),
54262306a36Sopenharmony_ci			test_alloc(test, size, GFP_KERNEL, ALLOCATE_LEFT),
54362306a36Sopenharmony_ci			test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE),
54462306a36Sopenharmony_ci			test_alloc(test, size, GFP_KERNEL, ALLOCATE_NONE),
54562306a36Sopenharmony_ci		};
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		kmem_cache_free_bulk(test_cache, ARRAY_SIZE(objects), objects);
54862306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, report_available());
54962306a36Sopenharmony_ci		test_cache_destroy();
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/* Test init-on-free works. */
55462306a36Sopenharmony_cistatic void test_init_on_free(struct kunit *test)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	const size_t size = 32;
55762306a36Sopenharmony_ci	struct expect_report expect = {
55862306a36Sopenharmony_ci		.type = KFENCE_ERROR_UAF,
55962306a36Sopenharmony_ci		.fn = test_init_on_free,
56062306a36Sopenharmony_ci		.is_write = false,
56162306a36Sopenharmony_ci	};
56262306a36Sopenharmony_ci	int i;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	KFENCE_TEST_REQUIRES(test, IS_ENABLED(CONFIG_INIT_ON_FREE_DEFAULT_ON));
56562306a36Sopenharmony_ci	/* Assume it hasn't been disabled on command line. */
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
56862306a36Sopenharmony_ci	expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
56962306a36Sopenharmony_ci	for (i = 0; i < size; i++)
57062306a36Sopenharmony_ci		expect.addr[i] = i + 1;
57162306a36Sopenharmony_ci	test_free(expect.addr);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
57462306a36Sopenharmony_ci		/*
57562306a36Sopenharmony_ci		 * This may fail if the page was recycled by KFENCE and then
57662306a36Sopenharmony_ci		 * written to again -- this however, is near impossible with a
57762306a36Sopenharmony_ci		 * default config.
57862306a36Sopenharmony_ci		 */
57962306a36Sopenharmony_ci		KUNIT_EXPECT_EQ(test, expect.addr[i], (char)0);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (!i) /* Only check first access to not fail test if page is ever re-protected. */
58262306a36Sopenharmony_ci			KUNIT_EXPECT_TRUE(test, report_matches(&expect));
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/* Ensure that constructors work properly. */
58762306a36Sopenharmony_cistatic void test_memcache_ctor(struct kunit *test)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	const size_t size = 32;
59062306a36Sopenharmony_ci	char *buf;
59162306a36Sopenharmony_ci	int i;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	setup_test_cache(test, size, 0, ctor_set_x);
59462306a36Sopenharmony_ci	buf = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
59762306a36Sopenharmony_ci		KUNIT_EXPECT_EQ(test, buf[i], (char)'x');
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	test_free(buf);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/* Test that memory is zeroed if requested. */
60562306a36Sopenharmony_cistatic void test_gfpzero(struct kunit *test)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	const size_t size = PAGE_SIZE; /* PAGE_SIZE so we can use ALLOCATE_ANY. */
60862306a36Sopenharmony_ci	char *buf1, *buf2;
60962306a36Sopenharmony_ci	int i;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* Skip if we think it'd take too long. */
61262306a36Sopenharmony_ci	KFENCE_TEST_REQUIRES(test, kfence_sample_interval <= 100);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
61562306a36Sopenharmony_ci	buf1 = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
61662306a36Sopenharmony_ci	for (i = 0; i < size; i++)
61762306a36Sopenharmony_ci		buf1[i] = i + 1;
61862306a36Sopenharmony_ci	test_free(buf1);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Try to get same address again -- this can take a while. */
62162306a36Sopenharmony_ci	for (i = 0;; i++) {
62262306a36Sopenharmony_ci		buf2 = test_alloc(test, size, GFP_KERNEL | __GFP_ZERO, ALLOCATE_ANY);
62362306a36Sopenharmony_ci		if (buf1 == buf2)
62462306a36Sopenharmony_ci			break;
62562306a36Sopenharmony_ci		test_free(buf2);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		if (kthread_should_stop() || (i == CONFIG_KFENCE_NUM_OBJECTS)) {
62862306a36Sopenharmony_ci			kunit_warn(test, "giving up ... cannot get same object back\n");
62962306a36Sopenharmony_ci			return;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci		cond_resched();
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	for (i = 0; i < size; i++)
63562306a36Sopenharmony_ci		KUNIT_EXPECT_EQ(test, buf2[i], (char)0);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	test_free(buf2);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic void test_invalid_access(struct kunit *test)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	const struct expect_report expect = {
64562306a36Sopenharmony_ci		.type = KFENCE_ERROR_INVALID,
64662306a36Sopenharmony_ci		.fn = test_invalid_access,
64762306a36Sopenharmony_ci		.addr = &__kfence_pool[10],
64862306a36Sopenharmony_ci		.is_write = false,
64962306a36Sopenharmony_ci	};
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	READ_ONCE(__kfence_pool[10]);
65262306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/* Test SLAB_TYPESAFE_BY_RCU works. */
65662306a36Sopenharmony_cistatic void test_memcache_typesafe_by_rcu(struct kunit *test)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	const size_t size = 32;
65962306a36Sopenharmony_ci	struct expect_report expect = {
66062306a36Sopenharmony_ci		.type = KFENCE_ERROR_UAF,
66162306a36Sopenharmony_ci		.fn = test_memcache_typesafe_by_rcu,
66262306a36Sopenharmony_ci		.is_write = false,
66362306a36Sopenharmony_ci	};
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	setup_test_cache(test, size, SLAB_TYPESAFE_BY_RCU, NULL);
66662306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, test_cache); /* Want memcache. */
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	expect.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY);
66962306a36Sopenharmony_ci	*expect.addr = 42;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	rcu_read_lock();
67262306a36Sopenharmony_ci	test_free(expect.addr);
67362306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test, *expect.addr, (char)42);
67462306a36Sopenharmony_ci	/*
67562306a36Sopenharmony_ci	 * Up to this point, memory should not have been freed yet, and
67662306a36Sopenharmony_ci	 * therefore there should be no KFENCE report from the above access.
67762306a36Sopenharmony_ci	 */
67862306a36Sopenharmony_ci	rcu_read_unlock();
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* Above access to @expect.addr should not have generated a report! */
68162306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Only after rcu_barrier() is the memory guaranteed to be freed. */
68462306a36Sopenharmony_ci	rcu_barrier();
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Expect use-after-free. */
68762306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test, *expect.addr, (char)42);
68862306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, report_matches(&expect));
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci/* Test krealloc(). */
69262306a36Sopenharmony_cistatic void test_krealloc(struct kunit *test)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	const size_t size = 32;
69562306a36Sopenharmony_ci	const struct expect_report expect = {
69662306a36Sopenharmony_ci		.type = KFENCE_ERROR_UAF,
69762306a36Sopenharmony_ci		.fn = test_krealloc,
69862306a36Sopenharmony_ci		.addr = test_alloc(test, size, GFP_KERNEL, ALLOCATE_ANY),
69962306a36Sopenharmony_ci		.is_write = false,
70062306a36Sopenharmony_ci	};
70162306a36Sopenharmony_ci	char *buf = expect.addr;
70262306a36Sopenharmony_ci	int i;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, test_cache);
70562306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test, ksize(buf), size); /* Precise size match after KFENCE alloc. */
70662306a36Sopenharmony_ci	for (i = 0; i < size; i++)
70762306a36Sopenharmony_ci		buf[i] = i + 1;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/* Check that we successfully change the size. */
71062306a36Sopenharmony_ci	buf = krealloc(buf, size * 3, GFP_KERNEL); /* Grow. */
71162306a36Sopenharmony_ci	/* Note: Might no longer be a KFENCE alloc. */
71262306a36Sopenharmony_ci	KUNIT_EXPECT_GE(test, ksize(buf), size * 3);
71362306a36Sopenharmony_ci	for (i = 0; i < size; i++)
71462306a36Sopenharmony_ci		KUNIT_EXPECT_EQ(test, buf[i], (char)(i + 1));
71562306a36Sopenharmony_ci	for (; i < size * 3; i++) /* Fill to extra bytes. */
71662306a36Sopenharmony_ci		buf[i] = i + 1;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	buf = krealloc(buf, size * 2, GFP_KERNEL); /* Shrink. */
71962306a36Sopenharmony_ci	KUNIT_EXPECT_GE(test, ksize(buf), size * 2);
72062306a36Sopenharmony_ci	for (i = 0; i < size * 2; i++)
72162306a36Sopenharmony_ci		KUNIT_EXPECT_EQ(test, buf[i], (char)(i + 1));
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	buf = krealloc(buf, 0, GFP_KERNEL); /* Free. */
72462306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test, (unsigned long)buf, (unsigned long)ZERO_SIZE_PTR);
72562306a36Sopenharmony_ci	KUNIT_ASSERT_FALSE(test, report_available()); /* No reports yet! */
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	READ_ONCE(*expect.addr); /* Ensure krealloc() actually freed earlier KFENCE object. */
72862306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, report_matches(&expect));
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/* Test that some objects from a bulk allocation belong to KFENCE pool. */
73262306a36Sopenharmony_cistatic void test_memcache_alloc_bulk(struct kunit *test)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	const size_t size = 32;
73562306a36Sopenharmony_ci	bool pass = false;
73662306a36Sopenharmony_ci	unsigned long timeout;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	setup_test_cache(test, size, 0, NULL);
73962306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, test_cache); /* Want memcache. */
74062306a36Sopenharmony_ci	/*
74162306a36Sopenharmony_ci	 * 100x the sample interval should be more than enough to ensure we get
74262306a36Sopenharmony_ci	 * a KFENCE allocation eventually.
74362306a36Sopenharmony_ci	 */
74462306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(100 * kfence_sample_interval);
74562306a36Sopenharmony_ci	do {
74662306a36Sopenharmony_ci		void *objects[100];
74762306a36Sopenharmony_ci		int i, num = kmem_cache_alloc_bulk(test_cache, GFP_ATOMIC, ARRAY_SIZE(objects),
74862306a36Sopenharmony_ci						   objects);
74962306a36Sopenharmony_ci		if (!num)
75062306a36Sopenharmony_ci			continue;
75162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(objects); i++) {
75262306a36Sopenharmony_ci			if (is_kfence_address(objects[i])) {
75362306a36Sopenharmony_ci				pass = true;
75462306a36Sopenharmony_ci				break;
75562306a36Sopenharmony_ci			}
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci		kmem_cache_free_bulk(test_cache, num, objects);
75862306a36Sopenharmony_ci		/*
75962306a36Sopenharmony_ci		 * kmem_cache_alloc_bulk() disables interrupts, and calling it
76062306a36Sopenharmony_ci		 * in a tight loop may not give KFENCE a chance to switch the
76162306a36Sopenharmony_ci		 * static branch. Call cond_resched() to let KFENCE chime in.
76262306a36Sopenharmony_ci		 */
76362306a36Sopenharmony_ci		cond_resched();
76462306a36Sopenharmony_ci	} while (!pass && time_before(jiffies, timeout));
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	KUNIT_EXPECT_TRUE(test, pass);
76762306a36Sopenharmony_ci	KUNIT_EXPECT_FALSE(test, report_available());
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci/*
77162306a36Sopenharmony_ci * KUnit does not provide a way to provide arguments to tests, and we encode
77262306a36Sopenharmony_ci * additional info in the name. Set up 2 tests per test case, one using the
77362306a36Sopenharmony_ci * default allocator, and another using a custom memcache (suffix '-memcache').
77462306a36Sopenharmony_ci */
77562306a36Sopenharmony_ci#define KFENCE_KUNIT_CASE(test_name)						\
77662306a36Sopenharmony_ci	{ .run_case = test_name, .name = #test_name },				\
77762306a36Sopenharmony_ci	{ .run_case = test_name, .name = #test_name "-memcache" }
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic struct kunit_case kfence_test_cases[] = {
78062306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_out_of_bounds_read),
78162306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_out_of_bounds_write),
78262306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_use_after_free_read),
78362306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_double_free),
78462306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_invalid_addr_free),
78562306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_corruption),
78662306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_free_bulk),
78762306a36Sopenharmony_ci	KFENCE_KUNIT_CASE(test_init_on_free),
78862306a36Sopenharmony_ci	KUNIT_CASE(test_kmalloc_aligned_oob_read),
78962306a36Sopenharmony_ci	KUNIT_CASE(test_kmalloc_aligned_oob_write),
79062306a36Sopenharmony_ci	KUNIT_CASE(test_shrink_memcache),
79162306a36Sopenharmony_ci	KUNIT_CASE(test_memcache_ctor),
79262306a36Sopenharmony_ci	KUNIT_CASE(test_invalid_access),
79362306a36Sopenharmony_ci	KUNIT_CASE(test_gfpzero),
79462306a36Sopenharmony_ci	KUNIT_CASE(test_memcache_typesafe_by_rcu),
79562306a36Sopenharmony_ci	KUNIT_CASE(test_krealloc),
79662306a36Sopenharmony_ci	KUNIT_CASE(test_memcache_alloc_bulk),
79762306a36Sopenharmony_ci	{},
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/* ===== End test cases ===== */
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int test_init(struct kunit *test)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	unsigned long flags;
80562306a36Sopenharmony_ci	int i;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (!__kfence_pool)
80862306a36Sopenharmony_ci		return -EINVAL;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	spin_lock_irqsave(&observed.lock, flags);
81162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(observed.lines); i++)
81262306a36Sopenharmony_ci		observed.lines[i][0] = '\0';
81362306a36Sopenharmony_ci	observed.nlines = 0;
81462306a36Sopenharmony_ci	spin_unlock_irqrestore(&observed.lock, flags);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* Any test with 'memcache' in its name will want a memcache. */
81762306a36Sopenharmony_ci	if (strstr(test->name, "memcache"))
81862306a36Sopenharmony_ci		test->priv = TEST_PRIV_WANT_MEMCACHE;
81962306a36Sopenharmony_ci	else
82062306a36Sopenharmony_ci		test->priv = NULL;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return 0;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic void test_exit(struct kunit *test)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	test_cache_destroy();
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cistatic int kfence_suite_init(struct kunit_suite *suite)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	register_trace_console(probe_console, NULL);
83362306a36Sopenharmony_ci	return 0;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic void kfence_suite_exit(struct kunit_suite *suite)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	unregister_trace_console(probe_console, NULL);
83962306a36Sopenharmony_ci	tracepoint_synchronize_unregister();
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic struct kunit_suite kfence_test_suite = {
84362306a36Sopenharmony_ci	.name = "kfence",
84462306a36Sopenharmony_ci	.test_cases = kfence_test_cases,
84562306a36Sopenharmony_ci	.init = test_init,
84662306a36Sopenharmony_ci	.exit = test_exit,
84762306a36Sopenharmony_ci	.suite_init = kfence_suite_init,
84862306a36Sopenharmony_ci	.suite_exit = kfence_suite_exit,
84962306a36Sopenharmony_ci};
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cikunit_test_suites(&kfence_test_suite);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
85462306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Potapenko <glider@google.com>, Marco Elver <elver@google.com>");
855