Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp3610879pxj; Tue, 11 May 2021 08:09:26 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzKug4qUKN2o/+Vb6VTiwwNb/m2GSAtINvLFvUrdd6Xe8gCIJquQrqPZ35SSXV1Ok6aQP9l X-Received: by 2002:a05:6512:3e14:: with SMTP id i20mr20680036lfv.142.1620745766087; Tue, 11 May 2021 08:09:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620745766; cv=none; d=google.com; s=arc-20160816; b=BMW7sk9xix5646I/CMDmXNOU3CsDLiyfk0HHNcp8uQf/pxjT+EawUsm406+3etCFcT FUVm2lwPT2IPfYgDRNPb96IiDG3L4G+APqHMPhty1OswOr4Ed6uoAskcuLK3zLDN1gX4 LEdO09QRSfSnEjnNMYqY+MCcxObNemPHU4yU1Bdvp0MFvqaZGXZaVE8YqJrVxU9FV0YR 4c6tkpJldDzEYgzgX5l0W4LlS5GYM5YeAZIu7yuvbl7tSs+mVAH2gFfUZgJQue6v6rQ5 EwrO7S14wmDjAPTabCjpukCKi0l5FE9hm0ygWHvZqBxMmemIt54zpMrNc2mRaTNPmMtj 0lrA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=mm7LDmbzuzXICr7GpzR9n7uELDGvRPAoxQvO706Q6EQ=; b=FoArN9VmyXvJccBl5hNGyQXvpTk+mahTizlG4Y6AgTyWz1MZw9JwFLKwLtyYZq0/C0 wVI6syN8REvTtgHFYCdaEQ9RHNzMpMaNDidM/kNeecAvftUKMX3TDB22iGtJyg7Xfs9V BUTDxn5qEtoS+IjFv+KibU0kvL6gz++spXE32dmTnfXJDHTVOC6Fn1GMI6XXSTcY23MK DhuemMw+e1vMyYbkt6bxplBy+d5BG3dNMyvUbR/oo1czpwHJP1psRXtGVvxxZy/IeJDD O7NtjlHeGQLajvqUDii3jUmOYYCDzCFtXibxEa0Hd3QbWAWLmx514NF8+C1QYXO23Bc5 Jb9g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=kpIm19si; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id l1si21434333ljj.580.2021.05.11.08.08.53; Tue, 11 May 2021 08:09:26 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=kpIm19si; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231769AbhEKPIs (ORCPT + 99 others); Tue, 11 May 2021 11:08:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57596 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231519AbhEKPIq (ORCPT ); Tue, 11 May 2021 11:08:46 -0400 Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com [IPv6:2a00:1450:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 73F99C06174A; Tue, 11 May 2021 08:07:39 -0700 (PDT) Received: by mail-ed1-x535.google.com with SMTP id n25so23293398edr.5; Tue, 11 May 2021 08:07:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mm7LDmbzuzXICr7GpzR9n7uELDGvRPAoxQvO706Q6EQ=; b=kpIm19siY4JZvc5ZWgrdvxs0Njlh0zJPd2HfCL6CU0PjadZ3E299V0Oy8QpgXoreWw dtm61EFtIV+JzzyS4OkyDrQ0/7ddR6V3wj+emih9GGmyEtPqMh95OdzWLUFWskaMrPf7 AKlJsUPSdr6V4IjXFy+vEQsTxzNxKbnHXnmgmtYEEwJGXxwku/C1zigcnA82v9R34rX9 y/u4kns2nMKxrNS9ADeU/3Y2p9UK2Xqd5wdsC5+UKgBb5ve3w5PQXHG1eTNYQKdwK7wV MEz2XLGhpiDSeYQ980ebNuJlMhXSo4jisWMOpRxfW8E8FSDJquKOSq+kpH6CEBL66NOW 4Qlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mm7LDmbzuzXICr7GpzR9n7uELDGvRPAoxQvO706Q6EQ=; b=H6QOKS/lUHD0LlzKwqV7u0wr1GwulFMatbwNBlybeNPoGNGCYJiomGJ2lObNb4b/2/ PzUCudrbQc6xHIZrlLTcZJzmPsVDuH3/y+DVZ9MtUWGsBYwZ4xYqkk8RriUf0PfoZElw SSXdfJ64X1kHzcR5QFnD4moPK3h3jYrozR/gvUa6LBh3kTFCnz2ZZ+GwSqjh/OXwften Z6xbBoLnTJ+aUsGd/lYHfqBS/sTF+PHlYXrASF8o6hMF5XPyZ33AR6kckf4gapUsJIrJ gZZHoZXi44W9y5RPdl/6eZxo11A9/DHpl15V4XZKRyC2BEzyh2QiPwfO1aa3zVQm9dn9 RcUA== X-Gm-Message-State: AOAM532NbsCmWOTSuyxdXZULJSQsBUmhmFkNHFHUVkotOl0PBfyUSvmU RVuCUcA5Q2gXbkXxbQc0cGU8QahO7/FVvA== X-Received: by 2002:aa7:d685:: with SMTP id d5mr37219744edr.200.1620745658110; Tue, 11 May 2021 08:07:38 -0700 (PDT) Received: from localhost.localdomain (ispc-static-34.84-47-111.telekom.sk. [84.47.111.34]) by smtp.gmail.com with ESMTPSA id lr15sm11872709ejb.107.2021.05.11.08.07.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 11 May 2021 08:07:37 -0700 (PDT) From: glittao@gmail.com To: brendanhiggins@google.com, cl@linux.com, penberg@kernel.org, rientjes@google.com, iamjoonsoo.kim@lge.com, akpm@linux-foundation.org, vbabka@suse.cz Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-mm@kvack.org, elver@google.com, dlatypov@google.com, Oliver Glitta Subject: [PATCH v5 2/3] mm/slub, kunit: add a KUnit test for SLUB debugging functionality Date: Tue, 11 May 2021 17:07:33 +0200 Message-Id: <20210511150734.3492-2-glittao@gmail.com> X-Mailer: git-send-email 2.31.1.272.g89b43f80a5 In-Reply-To: <20210511150734.3492-1-glittao@gmail.com> References: <20210511150734.3492-1-glittao@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Oliver Glitta SLUB has resiliency_test() function which is hidden behind #ifdef SLUB_RESILIENCY_TEST that is not part of Kconfig, so nobody runs it. KUnit should be a proper replacement for it. Try changing byte in redzone after allocation and changing pointer to next free node, first byte, 50th byte and redzone byte. Check if validation finds errors. There are several differences from the original resiliency test: Tests create own caches with known state instead of corrupting shared kmalloc caches. The corruption of freepointer uses correct offset, the original resiliency test got broken with freepointer changes. Scratch changing random byte test, because it does not have meaning in this form where we need deterministic results. Add new option CONFIG_SLUB_KUNIT_TEST in Kconfig. Tests next_pointer, first_word and clobber_50th_byte do not run with KASAN option on. Because the test deliberately modifies non-allocated objects. Use kunit_resource to count errors in cache and silence bug reports. Count error whenever slab_bug() or slab_fix() is called or when the count of pages is wrong. Signed-off-by: Oliver Glitta --- Changes since v4 Use two tests with KASAN dependency. Remove setting current test during init and exit. Changes since v3 Use kunit_resource to silence bug reports and count errors suggested by Marco Elver. Make the test depends on !KASAN thanks to report from the kernel test robot. Changes since v2 Use bit operation & instead of logical && as reported by kernel test robot and Dan Carpenter Changes since v1 Conversion from kselftest to KUnit test suggested by Marco Elver. Error silencing. Error counting improvements. lib/Kconfig.debug | 12 ++++ lib/Makefile | 1 + lib/slub_kunit.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++ mm/slab.h | 1 + mm/slub.c | 46 +++++++++++++- 5 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 lib/slub_kunit.c diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 678c13967580..7723f58a9394 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2429,6 +2429,18 @@ config BITS_TEST If unsure, say N. +config SLUB_KUNIT_TEST + tristate "KUnit test for SLUB cache error detection" if !KUNIT_ALL_TESTS + depends on SLUB_DEBUG && KUNIT + default KUNIT_ALL_TESTS + help + This builds SLUB allocator unit test. + Tests SLUB cache debugging functionality. + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config TEST_UDELAY tristate "udelay test driver" help diff --git a/lib/Makefile b/lib/Makefile index e11cfc18b6c0..386215dcb0a0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -353,5 +353,6 @@ obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o obj-$(CONFIG_BITS_TEST) += test_bits.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o +obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c new file mode 100644 index 000000000000..f28965f64ef6 --- /dev/null +++ b/lib/slub_kunit.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include "../mm/slab.h" + +static struct kunit_resource resource; +static int slab_errors; + +static void test_clobber_zone(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_alloc", 64, 0, + SLAB_RED_ZONE, NULL); + u8 *p = kmem_cache_alloc(s, GFP_KERNEL); + + kasan_disable_current(); + p[64] = 0x12; + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + kasan_enable_current(); + kmem_cache_free(s, p); + kmem_cache_destroy(s); +} + +#ifndef CONFIG_KASAN +static void test_next_pointer(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_next_ptr_free", 64, 0, + SLAB_POISON, NULL); + u8 *p = kmem_cache_alloc(s, GFP_KERNEL); + unsigned long tmp; + unsigned long *ptr_addr; + + kmem_cache_free(s, p); + + ptr_addr = (unsigned long *)(p + s->offset); + tmp = *ptr_addr; + p[s->offset] = 0x12; + + /* + * Expecting three errors. + * One for the corrupted freechain and the other one for the wrong + * count of objects in use. The third error is fixing broken cache. + */ + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 3, slab_errors); + + /* + * Try to repair corrupted freepointer. + * Still expecting two errors. The first for the wrong count + * of objects in use. + * The second error is for fixing broken cache. + */ + *ptr_addr = tmp; + slab_errors = 0; + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + /* + * Previous validation repaired the count of objects in use. + * Now expecting no error. + */ + slab_errors = 0; + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 0, slab_errors); + + kmem_cache_destroy(s); +} + +static void test_first_word(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_1th_word_free", 64, 0, + SLAB_POISON, NULL); + u8 *p = kmem_cache_alloc(s, GFP_KERNEL); + + kmem_cache_free(s, p); + *p = 0x78; + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + kmem_cache_destroy(s); +} + +static void test_clobber_50th_byte(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_50th_word_free", 64, 0, + SLAB_POISON, NULL); + u8 *p = kmem_cache_alloc(s, GFP_KERNEL); + + kmem_cache_free(s, p); + p[50] = 0x9a; + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + kmem_cache_destroy(s); +} +#endif + +static void test_clobber_redzone_free(struct kunit *test) +{ + struct kmem_cache *s = kmem_cache_create("TestSlub_RZ_free", 64, 0, + SLAB_RED_ZONE, NULL); + u8 *p = kmem_cache_alloc(s, GFP_KERNEL); + + kasan_disable_current(); + kmem_cache_free(s, p); + p[64] = 0xab; + + validate_slab_cache(s); + KUNIT_EXPECT_EQ(test, 2, slab_errors); + + kasan_enable_current(); + kmem_cache_destroy(s); +} + +static int test_init(struct kunit *test) +{ + slab_errors = 0; + + kunit_add_named_resource(test, NULL, NULL, &resource, + "slab_errors", &slab_errors); + return 0; +} + +static void test_exit(struct kunit *test) {} + +static struct kunit_case test_cases[] = { + KUNIT_CASE(test_clobber_zone), + +#ifndef CONFIG_KASAN + KUNIT_CASE(test_next_pointer), + KUNIT_CASE(test_first_word), + KUNIT_CASE(test_clobber_50th_byte), +#endif + + KUNIT_CASE(test_clobber_redzone_free), + {} +}; + +static struct kunit_suite test_suite = { + .name = "slub_test", + .init = test_init, + .exit = test_exit, + .test_cases = test_cases, +}; +kunit_test_suite(test_suite); + +MODULE_LICENSE("GPL"); diff --git a/mm/slab.h b/mm/slab.h index 18c1927cd196..9b690fa44cae 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -215,6 +215,7 @@ DECLARE_STATIC_KEY_TRUE(slub_debug_enabled); DECLARE_STATIC_KEY_FALSE(slub_debug_enabled); #endif extern void print_tracking(struct kmem_cache *s, void *object); +long validate_slab_cache(struct kmem_cache *s); #else static inline void print_tracking(struct kmem_cache *s, void *object) { diff --git a/mm/slub.c b/mm/slub.c index feda53ae62ba..985fd6ef033c 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -447,6 +448,26 @@ static inline bool cmpxchg_double_slab(struct kmem_cache *s, struct page *page, static unsigned long object_map[BITS_TO_LONGS(MAX_OBJS_PER_PAGE)]; static DEFINE_SPINLOCK(object_map_lock); +#if IS_ENABLED(CONFIG_KUNIT) +static bool slab_add_kunit_errors(void) +{ + struct kunit_resource *resource; + + if (likely(!current->kunit_test)) + return false; + + resource = kunit_find_named_resource(current->kunit_test, "slab_errors"); + if (!resource) + return false; + + (*(int *)resource->data)++; + kunit_put_resource(resource); + return true; +} +#else +static inline bool slab_add_kunit_errors(void) { return false; } +#endif + /* * Determine a map of object in use on a page. * @@ -677,6 +698,9 @@ static void slab_fix(struct kmem_cache *s, char *fmt, ...) struct va_format vaf; va_list args; + if (slab_add_kunit_errors()) + return; + va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; @@ -740,6 +764,9 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p) void object_err(struct kmem_cache *s, struct page *page, u8 *object, char *reason) { + if (slab_add_kunit_errors()) + return; + slab_bug(s, "%s", reason); print_trailer(s, page, object); } @@ -750,6 +777,9 @@ static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page, va_list args; char buf[100]; + if (slab_add_kunit_errors()) + return; + va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); @@ -799,12 +829,16 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, while (end > fault && end[-1] == value) end--; + if (slab_add_kunit_errors()) + goto skip_bug_print; + slab_bug(s, "%s overwritten", what); pr_err("0x%p-0x%p @offset=%tu. First byte 0x%x instead of 0x%x\n", fault, end - 1, fault - addr, fault[0], value); print_trailer(s, page, object); +skip_bug_print: restore_bytes(s, what, value, fault, end); return 0; } @@ -4662,9 +4696,11 @@ static int validate_slab_node(struct kmem_cache *s, validate_slab(s, page); count++; } - if (count != n->nr_partial) + if (count != n->nr_partial) { pr_err("SLUB %s: %ld partial slabs counted but counter=%ld\n", s->name, count, n->nr_partial); + slab_add_kunit_errors(); + } if (!(s->flags & SLAB_STORE_USER)) goto out; @@ -4673,16 +4709,18 @@ static int validate_slab_node(struct kmem_cache *s, validate_slab(s, page); count++; } - if (count != atomic_long_read(&n->nr_slabs)) + if (count != atomic_long_read(&n->nr_slabs)) { pr_err("SLUB: %s %ld slabs counted but counter=%ld\n", s->name, count, atomic_long_read(&n->nr_slabs)); + slab_add_kunit_errors(); + } out: spin_unlock_irqrestore(&n->list_lock, flags); return count; } -static long validate_slab_cache(struct kmem_cache *s) +long validate_slab_cache(struct kmem_cache *s) { int node; unsigned long count = 0; @@ -4694,6 +4732,8 @@ static long validate_slab_cache(struct kmem_cache *s) return count; } +EXPORT_SYMBOL(validate_slab_cache); + /* * Generate lists of code addresses where slabcache objects are allocated * and freed. -- 2.31.1.272.g89b43f80a5