From patchwork Mon Nov 6 20:10:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: andrey.konovalov@linux.dev X-Patchwork-Id: 13447463 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 60480C0018A for ; Mon, 6 Nov 2023 20:12:53 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 377906B02A3; Mon, 6 Nov 2023 15:12:49 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 302D96B02A4; Mon, 6 Nov 2023 15:12:49 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id EBF956B02A5; Mon, 6 Nov 2023 15:12:48 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id D0C696B02A4 for ; Mon, 6 Nov 2023 15:12:48 -0500 (EST) Received: from smtpin15.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay07.hostedemail.com (Postfix) with ESMTP id A66281606C6 for ; Mon, 6 Nov 2023 20:12:48 +0000 (UTC) X-FDA: 81428627616.15.0A46F6D Received: from out-177.mta1.migadu.com (out-177.mta1.migadu.com [95.215.58.177]) by imf10.hostedemail.com (Postfix) with ESMTP id 00302C000A for ; Mon, 6 Nov 2023 20:12:46 +0000 (UTC) Authentication-Results: imf10.hostedemail.com; dkim=pass header.d=linux.dev header.s=key1 header.b="u66VHtu/"; spf=pass (imf10.hostedemail.com: domain of andrey.konovalov@linux.dev designates 95.215.58.177 as permitted sender) smtp.mailfrom=andrey.konovalov@linux.dev; dmarc=pass (policy=none) header.from=linux.dev ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1699301567; a=rsa-sha256; cv=none; b=vOrPFNAymLFETqIQUsHp9vk86OHypthv5jB2K7fh30NXZsKbPyRhFO+3olMsJYZVMrHjs2 JJWyZQkssx1LHn2YWiuQC07ga3CA2cue0SdvdIlXZiOxkR3BNOvFqpfv7/V9Sl+N7zy/m5 5qNzxOTyrr5senkJZEY00DkrNY5cNkQ= ARC-Authentication-Results: i=1; imf10.hostedemail.com; dkim=pass header.d=linux.dev header.s=key1 header.b="u66VHtu/"; spf=pass (imf10.hostedemail.com: domain of andrey.konovalov@linux.dev designates 95.215.58.177 as permitted sender) smtp.mailfrom=andrey.konovalov@linux.dev; dmarc=pass (policy=none) header.from=linux.dev ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1699301567; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=J0FZGClDjGLvLD3TemnnZlVkckK/FbBcRM13VFu32PM=; b=FkXWb7nU1VM89qY6IBmL+AfsCllhE5JpG9exV7gbGDdcoGATQo1Qa97OywBxcH1U6QY8ky qH9ZPlAU+vzUdKjsadLYjcdt3rByceL/X/HZxtZzxF+FkQrrTI+gA7WDIZGCL8T1kELncp t7WF5f4TGa62tkaS3JIyiLbRWl24OtE= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1699301565; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=J0FZGClDjGLvLD3TemnnZlVkckK/FbBcRM13VFu32PM=; b=u66VHtu/jQ8awmrPx0sqAT3awrwRy7LoUZT6YuP/AeVFLTV8z6gU99EDSnmB7b+vlP8GiH kLVdU8feP/LvgjDFYRFlJYBJnYVG/BCUZebuMkwHoBpVhV+QpImR3lWmGnjoTkYHo9queI m9qLnEnZnW6Qbh8fHp4S9bL0AaSkd1M= From: andrey.konovalov@linux.dev To: Marco Elver , Alexander Potapenko Cc: Andrey Konovalov , Dmitry Vyukov , Andrey Ryabinin , kasan-dev@googlegroups.com, Evgenii Stepanov , Andrew Morton , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Andrey Konovalov Subject: [PATCH RFC 15/20] kasan: add mempool tests Date: Mon, 6 Nov 2023 21:10:24 +0100 Message-Id: <389467628f04e7defb81cc08079cdc9c983f71a4.1699297309.git.andreyknvl@google.com> In-Reply-To: References: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT X-Rspamd-Server: rspam08 X-Rspamd-Queue-Id: 00302C000A X-Stat-Signature: 4it9k6aidns3hedtoxckuihzxub9u4w8 X-Rspam-User: X-HE-Tag: 1699301566-344404 X-HE-Meta: U2FsdGVkX1/A9nj1YCaFh3rtMUxqET8FCU7rT9WNhO5K+1u55xyR2uaw74x5nUJulsuuxLlgHUSRhPYJCD2efJM7OldMU4HMUrffm4lb9Nhurkn1pMGeDOZwNpok6zKBxSAgrNH1U46Gd6fk0i/H+6/xaxwHUiSPjy/9J2lo0cjMMIh8XbuMKLuGaL7YDaRlu6K/nh5GcrsYkrbZfTC7aMBJc0hPOWOCo5G5tuW2GvGXrjPIhKxwFTUIEeaV4R8xfYZu9YBtMbZRJu67ojsRgQHzM4bYO42xu8P6veiE2c9W3CYRDL6RHkhZ1q2SyWjUfET8No2c0lK6lxbkNmkoSUi/Rw7/VLO67JWSUSVGVm9SP7/gC4UZJJhNRm55sYCZhlKRnrC+4dZ9YxgvVypuM3uLSviAOHlO/8sbATJd3ZzzDLm34mPXAFem6KaWJZfTZWy4PNXG+vS1T3HWeVWSeq0TKnyDRjUNF1as2nf73Ta8qhBJvtg7OSrD1ZT0TiunS6o9V0TUp/eyCueNlfqX+pw+oGjt5G5hwNXIQFO8FsJj4KqRxEcREYZnRTxx4rhQZw2Y3rEPkppFs4l9LYHK+8Iy3StnhVWj2l5RBlTWQZwx1K1oy9XdBNPePEQ+RalcDj2haWI1nvhUMT+hfsvWLSaAMHLLd/5Kl1/U0nt4AW+goYdmg3UPYYkWXWU2xGRrUVkEEVe5mqQ+CzlII4G2n14HbYQLYYdczW/csxkXeusHoFlQ0g5OwvlGWPPQNLSo7TIpSZoS6GEzEfdZ1EUylZpA9iJ1fr7k8qcin5wn9iXkgMihNQW+njPzT5cXOrAeCJYI1CehYoZ5K1oyE6xmGIsU9N++94yVuBqW74WudDk3PFoFWP21lX4TtD98HCnS8za+BWQOxyh7HHravZ9IYKJbvuwx8cwXAurueOVrZVCmTFD8ZpSYlY8vjy6RgDIIhjnMPBxagt5cJ96HfGw ZKWRBEPv wORcJpIZakB/6htOK7M/K6etmdg8D9aSdQvgofSvfz/RShX1Vvnm1hHwMzXUo6Xa7UydDhL+r1E+SaMbTMa4Y6NmHZIPGxiLr/4gSbUenRdV+QBkZoV4aTlqWwN0Mamrv24sNajJISkz4ni7wrcZ2Ouz/Ln8dwrT4zGjfv/v1VzoJod+tMgcHw9P2k+x61pfM+0chYdJm1/d/uDuLW4+EM23ykaPQZrc1hdNpxmd23+25uHU= X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: From: Andrey Konovalov Add KASAN tests for mempool. Signed-off-by: Andrey Konovalov --- mm/kasan/kasan_test.c | 325 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) diff --git a/mm/kasan/kasan_test.c b/mm/kasan/kasan_test.c index 8281eb42464b..9adbcd04259b 100644 --- a/mm/kasan/kasan_test.c +++ b/mm/kasan/kasan_test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -798,6 +799,318 @@ static void kmem_cache_bulk(struct kunit *test) kmem_cache_destroy(cache); } +static void *mempool_prepare_kmalloc(struct kunit *test, mempool_t *pool, size_t size) +{ + int pool_size = 4; + int ret; + void *elem; + + memset(pool, 0, sizeof(*pool)); + ret = mempool_init_kmalloc_pool(pool, pool_size, size); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Tell mempool to only use preallocated elements. */ + mempool_use_prealloc_only(pool); + + /* + * Allocate one element to prevent mempool from freeing elements to the + * underlying allocator and instead make it add them to the element + * list when the tests trigger double-free and invalid-free bugs. + * This allows testing KASAN annotations in add_element(). + */ + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + return elem; +} + +static struct kmem_cache *mempool_prepare_slab(struct kunit *test, mempool_t *pool, size_t size) +{ + struct kmem_cache *cache; + int pool_size = 4; + int ret; + + cache = kmem_cache_create("test_cache", size, 0, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cache); + + memset(pool, 0, sizeof(*pool)); + ret = mempool_init_slab_pool(pool, pool_size, cache); + KUNIT_ASSERT_EQ(test, ret, 0); + + mempool_use_prealloc_only(pool); + + /* + * Do not allocate one preallocated element, as we skip the double-free + * and invalid-free tests for slab mempool for simplicity. + */ + + return cache; +} + +static void *mempool_prepare_page(struct kunit *test, mempool_t *pool, int order) +{ + int pool_size = 4; + int ret; + void *elem; + + memset(pool, 0, sizeof(*pool)); + ret = mempool_init_page_pool(pool, pool_size, order); + KUNIT_ASSERT_EQ(test, ret, 0); + + mempool_use_prealloc_only(pool); + + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + return elem; +} + +static void mempool_oob_right_helper(struct kunit *test, mempool_t *pool, size_t size) +{ + char *elem; + + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + OPTIMIZER_HIDE_VAR(elem); + + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) + KUNIT_EXPECT_KASAN_FAIL(test, elem[size] = 'x'); + else + KUNIT_EXPECT_KASAN_FAIL(test, + elem[round_up(size, KASAN_GRANULE_SIZE)] = 'x'); + + mempool_free(elem, pool); +} + +static void mempool_kmalloc_oob_right(struct kunit *test) +{ + mempool_t pool; + size_t size = 128 - KASAN_GRANULE_SIZE - 5; + void *extra_elem; + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_oob_right_helper(test, &pool, size); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_kmalloc_large_oob_right(struct kunit *test) +{ + mempool_t pool; + size_t size = KMALLOC_MAX_CACHE_SIZE + 1; + void *extra_elem; + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_oob_right_helper(test, &pool, size); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_slab_oob_right(struct kunit *test) +{ + mempool_t pool; + size_t size = 123; + struct kmem_cache *cache; + + cache = mempool_prepare_slab(test, &pool, size); + + mempool_oob_right_helper(test, &pool, size); + + mempool_exit(&pool); + kmem_cache_destroy(cache); +} + +/* + * Skip the out-of-bounds test for page mempool. With Generic KASAN, page + * allocations have no redzones, and thus the out-of-bounds detection is not + * guaranteed; see https://bugzilla.kernel.org/show_bug.cgi?id=210503. With + * the tag-based KASAN modes, the neighboring allocation might have the same + * tag; see https://bugzilla.kernel.org/show_bug.cgi?id=203505. + */ + +static void mempool_uaf_helper(struct kunit *test, mempool_t *pool, bool page) +{ + char *elem, *ptr; + + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + mempool_free(elem, pool); + + ptr = page ? page_address((struct page *)elem) : elem; + KUNIT_EXPECT_KASAN_FAIL(test, ((volatile char *)ptr)[0]); +} + +static void mempool_kmalloc_uaf(struct kunit *test) +{ + mempool_t pool; + size_t size = 128; + void *extra_elem; + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_uaf_helper(test, &pool, false); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_kmalloc_large_uaf(struct kunit *test) +{ + mempool_t pool; + size_t size = KMALLOC_MAX_CACHE_SIZE + 1; + void *extra_elem; + + /* page_alloc fallback is only implemented for SLUB. */ + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_SLUB); + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_uaf_helper(test, &pool, false); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_slab_uaf(struct kunit *test) +{ + mempool_t pool; + size_t size = 123; + struct kmem_cache *cache; + + cache = mempool_prepare_slab(test, &pool, size); + + mempool_uaf_helper(test, &pool, false); + + mempool_exit(&pool); + kmem_cache_destroy(cache); +} + +static void mempool_page_alloc_uaf(struct kunit *test) +{ + mempool_t pool; + int order = 2; + void *extra_elem; + + extra_elem = mempool_prepare_page(test, &pool, order); + + mempool_uaf_helper(test, &pool, true); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_double_free_helper(struct kunit *test, mempool_t *pool) +{ + char *elem; + + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + mempool_free(elem, pool); + + KUNIT_EXPECT_KASAN_FAIL(test, mempool_free(elem, pool)); +} + +static void mempool_kmalloc_double_free(struct kunit *test) +{ + mempool_t pool; + size_t size = 128; + char *extra_elem; + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_double_free_helper(test, &pool); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_kmalloc_large_double_free(struct kunit *test) +{ + mempool_t pool; + size_t size = KMALLOC_MAX_CACHE_SIZE + 1; + char *extra_elem; + + /* page_alloc fallback is only implemented for SLUB. */ + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_SLUB); + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_double_free_helper(test, &pool); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_page_alloc_double_free(struct kunit *test) +{ + mempool_t pool; + int order = 2; + char *extra_elem; + + extra_elem = mempool_prepare_page(test, &pool, order); + + mempool_double_free_helper(test, &pool); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_kmalloc_invalid_free_helper(struct kunit *test, mempool_t *pool) +{ + char *elem; + + elem = mempool_alloc(pool, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elem); + + KUNIT_EXPECT_KASAN_FAIL(test, mempool_free(elem + 1, pool)); + + mempool_free(elem, pool); +} + +static void mempool_kmalloc_invalid_free(struct kunit *test) +{ + mempool_t pool; + size_t size = 128; + char *extra_elem; + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_kmalloc_invalid_free_helper(test, &pool); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +static void mempool_kmalloc_large_invalid_free(struct kunit *test) +{ + mempool_t pool; + size_t size = KMALLOC_MAX_CACHE_SIZE + 1; + char *extra_elem; + + /* page_alloc fallback is only implemented for SLUB. */ + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_SLUB); + + extra_elem = mempool_prepare_kmalloc(test, &pool, size); + + mempool_kmalloc_invalid_free_helper(test, &pool); + + mempool_free(extra_elem, &pool); + mempool_exit(&pool); +} + +/* + * Skip the invalid-free test for page mempool. The invalid-free detection only + * works for compound pages and mempool preallocates all page elements without + * the __GFP_COMP flag. + */ + static char global_array[10]; static void kasan_global_oob_right(struct kunit *test) @@ -1538,6 +1851,18 @@ static struct kunit_case kasan_kunit_test_cases[] = { KUNIT_CASE(kmem_cache_oob), KUNIT_CASE(kmem_cache_accounted), KUNIT_CASE(kmem_cache_bulk), + KUNIT_CASE(mempool_kmalloc_oob_right), + KUNIT_CASE(mempool_kmalloc_large_oob_right), + KUNIT_CASE(mempool_slab_oob_right), + KUNIT_CASE(mempool_kmalloc_uaf), + KUNIT_CASE(mempool_kmalloc_large_uaf), + KUNIT_CASE(mempool_slab_uaf), + KUNIT_CASE(mempool_page_alloc_uaf), + KUNIT_CASE(mempool_kmalloc_double_free), + KUNIT_CASE(mempool_kmalloc_large_double_free), + KUNIT_CASE(mempool_page_alloc_double_free), + KUNIT_CASE(mempool_kmalloc_invalid_free), + KUNIT_CASE(mempool_kmalloc_large_invalid_free), KUNIT_CASE(kasan_global_oob_right), KUNIT_CASE(kasan_global_oob_left), KUNIT_CASE(kasan_stack_oob),