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