162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <kunit/test.h> 362306a36Sopenharmony_ci#include <kunit/test-bug.h> 462306a36Sopenharmony_ci#include <linux/mm.h> 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include "../mm/slab.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic struct kunit_resource resource; 1162306a36Sopenharmony_cistatic int slab_errors; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Wrapper function for kmem_cache_create(), which reduces 2 parameters: 1562306a36Sopenharmony_ci * 'align' and 'ctor', and sets SLAB_SKIP_KFENCE flag to avoid getting an 1662306a36Sopenharmony_ci * object from kfence pool, where the operation could be caught by both 1762306a36Sopenharmony_ci * our test and kfence sanity check. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic struct kmem_cache *test_kmem_cache_create(const char *name, 2062306a36Sopenharmony_ci unsigned int size, slab_flags_t flags) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct kmem_cache *s = kmem_cache_create(name, size, 0, 2362306a36Sopenharmony_ci (flags | SLAB_NO_USER_FLAGS), NULL); 2462306a36Sopenharmony_ci s->flags |= SLAB_SKIP_KFENCE; 2562306a36Sopenharmony_ci return s; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void test_clobber_zone(struct kunit *test) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_alloc", 64, 3162306a36Sopenharmony_ci SLAB_RED_ZONE); 3262306a36Sopenharmony_ci u8 *p = kmem_cache_alloc(s, GFP_KERNEL); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci kasan_disable_current(); 3562306a36Sopenharmony_ci p[64] = 0x12; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci validate_slab_cache(s); 3862306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci kasan_enable_current(); 4162306a36Sopenharmony_ci kmem_cache_free(s, p); 4262306a36Sopenharmony_ci kmem_cache_destroy(s); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#ifndef CONFIG_KASAN 4662306a36Sopenharmony_cistatic void test_next_pointer(struct kunit *test) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_next_ptr_free", 4962306a36Sopenharmony_ci 64, SLAB_POISON); 5062306a36Sopenharmony_ci u8 *p = kmem_cache_alloc(s, GFP_KERNEL); 5162306a36Sopenharmony_ci unsigned long tmp; 5262306a36Sopenharmony_ci unsigned long *ptr_addr; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci kmem_cache_free(s, p); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ptr_addr = (unsigned long *)(p + s->offset); 5762306a36Sopenharmony_ci tmp = *ptr_addr; 5862306a36Sopenharmony_ci p[s->offset] = 0x12; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Expecting three errors. 6262306a36Sopenharmony_ci * One for the corrupted freechain and the other one for the wrong 6362306a36Sopenharmony_ci * count of objects in use. The third error is fixing broken cache. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci validate_slab_cache(s); 6662306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 3, slab_errors); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Try to repair corrupted freepointer. 7062306a36Sopenharmony_ci * Still expecting two errors. The first for the wrong count 7162306a36Sopenharmony_ci * of objects in use. 7262306a36Sopenharmony_ci * The second error is for fixing broken cache. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci *ptr_addr = tmp; 7562306a36Sopenharmony_ci slab_errors = 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci validate_slab_cache(s); 7862306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * Previous validation repaired the count of objects in use. 8262306a36Sopenharmony_ci * Now expecting no error. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci slab_errors = 0; 8562306a36Sopenharmony_ci validate_slab_cache(s); 8662306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 0, slab_errors); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci kmem_cache_destroy(s); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void test_first_word(struct kunit *test) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_1th_word_free", 9462306a36Sopenharmony_ci 64, SLAB_POISON); 9562306a36Sopenharmony_ci u8 *p = kmem_cache_alloc(s, GFP_KERNEL); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci kmem_cache_free(s, p); 9862306a36Sopenharmony_ci *p = 0x78; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci validate_slab_cache(s); 10162306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci kmem_cache_destroy(s); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void test_clobber_50th_byte(struct kunit *test) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_50th_word_free", 10962306a36Sopenharmony_ci 64, SLAB_POISON); 11062306a36Sopenharmony_ci u8 *p = kmem_cache_alloc(s, GFP_KERNEL); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci kmem_cache_free(s, p); 11362306a36Sopenharmony_ci p[50] = 0x9a; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci validate_slab_cache(s); 11662306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci kmem_cache_destroy(s); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void test_clobber_redzone_free(struct kunit *test) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_free", 64, 12562306a36Sopenharmony_ci SLAB_RED_ZONE); 12662306a36Sopenharmony_ci u8 *p = kmem_cache_alloc(s, GFP_KERNEL); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci kasan_disable_current(); 12962306a36Sopenharmony_ci kmem_cache_free(s, p); 13062306a36Sopenharmony_ci p[64] = 0xab; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci validate_slab_cache(s); 13362306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci kasan_enable_current(); 13662306a36Sopenharmony_ci kmem_cache_destroy(s); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void test_kmalloc_redzone_access(struct kunit *test) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32, 14262306a36Sopenharmony_ci SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE); 14362306a36Sopenharmony_ci u8 *p = kmalloc_trace(s, GFP_KERNEL, 18); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci kasan_disable_current(); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Suppress the -Warray-bounds warning */ 14862306a36Sopenharmony_ci OPTIMIZER_HIDE_VAR(p); 14962306a36Sopenharmony_ci p[18] = 0xab; 15062306a36Sopenharmony_ci p[19] = 0xab; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci validate_slab_cache(s); 15362306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, 2, slab_errors); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci kasan_enable_current(); 15662306a36Sopenharmony_ci kmem_cache_free(s, p); 15762306a36Sopenharmony_ci kmem_cache_destroy(s); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int test_init(struct kunit *test) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci slab_errors = 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci kunit_add_named_resource(test, NULL, NULL, &resource, 16562306a36Sopenharmony_ci "slab_errors", &slab_errors); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct kunit_case test_cases[] = { 17062306a36Sopenharmony_ci KUNIT_CASE(test_clobber_zone), 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#ifndef CONFIG_KASAN 17362306a36Sopenharmony_ci KUNIT_CASE(test_next_pointer), 17462306a36Sopenharmony_ci KUNIT_CASE(test_first_word), 17562306a36Sopenharmony_ci KUNIT_CASE(test_clobber_50th_byte), 17662306a36Sopenharmony_ci#endif 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci KUNIT_CASE(test_clobber_redzone_free), 17962306a36Sopenharmony_ci KUNIT_CASE(test_kmalloc_redzone_access), 18062306a36Sopenharmony_ci {} 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic struct kunit_suite test_suite = { 18462306a36Sopenharmony_ci .name = "slub_test", 18562306a36Sopenharmony_ci .init = test_init, 18662306a36Sopenharmony_ci .test_cases = test_cases, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_cikunit_test_suite(test_suite); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 191