From patchwork Tue Mar 14 11:50:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Rutland X-Patchwork-Id: 69546 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:5915:0:0:0:0:0 with SMTP id v21csp1720185wrd; Tue, 14 Mar 2023 05:14:51 -0700 (PDT) X-Google-Smtp-Source: AK7set9HmxoZosuobchKh7zw4mGYpVCMyhIXNIcXkmvRBQCcJk5t4PBTopFWzi+ieYoVFYhViulQ X-Received: by 2002:a17:90b:1e01:b0:237:39b1:7cb1 with SMTP id pg1-20020a17090b1e0100b0023739b17cb1mr38676348pjb.19.1678796090804; Tue, 14 Mar 2023 05:14:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1678796090; cv=none; d=google.com; s=arc-20160816; b=t9gZQ3LwMM82fGB+tnPw1WinvPuyHtbh/uWDOiELPafEmYf3R60H1eEof3sBtjF+13 RU5QTx/rbu/wk6dKMhj6+/1BJ4Gc6vkXeUWBU9PmE12qUTK9ONXvqCD6yPrJ8VTQrFwX /VXRdmTAto8ns3X+d9XmxtciWKvxoJaEEGB4VFTB7ovo2r65F9GrFKtduR9zSWIpuhIn OhHXsPNGRT2s9cf+qsNLBKWX01r8I06pS/6UYUHk3uYWiAGE54iiR1rGbTmgz3fSf3GT vj3N7GtrY3ukzkgytLDXrrUpCkRrAthCy40gae45rQ2NxgDg0u56oVI2W0r9Sf7tEGM0 z5Cw== 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; bh=YE47Ph7rjVwMnbHSRHyuvgCRvXz1piffJxQ7hPpGPk0=; b=Qk8XNmkgxoH5TlDdG0hZp4oHr3Z5LyiFaZWxcLwv4USKyJnd/YoqbcsNIVbB+k8Dou CELfGN6zUjFejiHQdm+YKrHaL6ZwFmPAU1I5bNDj5yZIS5UfkiVm12EIeoBJNpFdowon uhgfQdxE5jQH914uDy9OBvXOq+ZEG6vGrRaSWR6q+7Wqnbbr4wXWVUUksRSTnpdAFvpP /NWlsR4UZJy3RmaM/pkDcAxY3LxULg8LOx0dmES3ksAoJ9Dqk772XRovsOGBajo9LRKO Vsd8xkmU7Ot+rG8yk1QRIAs54ip2IJOPFVBWi/QajnbPdrvB8567G8nDMemOQxi2qNsy 1CyQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id oe8-20020a17090b394800b002263b1df9f7si1706254pjb.0.2023.03.14.05.14.35; Tue, 14 Mar 2023 05:14:50 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231506AbjCNLu4 (ORCPT + 99 others); Tue, 14 Mar 2023 07:50:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37140 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230173AbjCNLur (ORCPT ); Tue, 14 Mar 2023 07:50:47 -0400 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 2C84E29E00 for ; Tue, 14 Mar 2023 04:50:46 -0700 (PDT) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 8A0CD1063; Tue, 14 Mar 2023 04:51:29 -0700 (PDT) Received: from lakrids.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id E009F3F67D; Tue, 14 Mar 2023 04:50:44 -0700 (PDT) From: Mark Rutland To: linux-arm-kernel@lists.infradead.org Cc: catalin.marinas@arm.com, linux-kernel@vger.kernel.org, mark.rutland@arm.com, robin.murphy@arm.com, viro@zeniv.linux.org.uk, will@kernel.org Subject: [PATCH 2/4] lib: test clear_user() Date: Tue, 14 Mar 2023 11:50:28 +0000 Message-Id: <20230314115030.347976-3-mark.rutland@arm.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230314115030.347976-1-mark.rutland@arm.com> References: <20230314115030.347976-1-mark.rutland@arm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760345289370052689?= X-GMAIL-MSGID: =?utf-8?q?1760345289370052689?= The clear_user() function follows the same conventions as copy_{to,from}_user(), and presumably has identical requirements on the return value. Test it in the same way. I've given this a spin on a few architectures using the KUnit QEMU harness, and it looks like most get *something* wrong, or I've misunderstood and clear_user() doesn't have the same requirements as copy_{to,from}_user()). From those initial runs: * arm, arm64, i386, riscv, x86_64 don't ensure that at least 1 byte is zeroed when a partial zeroing is possible, e.g. | too few bytes consumed (offset=4095, size=2, ret=2) | too few bytes consumed (offset=4093, size=4, ret=4) | too few bytes consumed (offset=4089, size=8, ret=8) * s390 reports that some bytes have been zeroed even when they haven't, e.g. | zeroed bytes incorrect (dst_page[4031+64]=0xca, offset=4031, size=66, ret=1 * sparc passses all tests Signed-off-by: Mark Rutland Cc: Al Viro Cc: Catalin Marinas Cc: Robin Murphy Cc: Will Deacon --- lib/usercopy_kunit.c | 89 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/lib/usercopy_kunit.c b/lib/usercopy_kunit.c index 0c73177b6ce5..8e037e3a46e3 100644 --- a/lib/usercopy_kunit.c +++ b/lib/usercopy_kunit.c @@ -152,6 +152,11 @@ void usercopy_test_exit(struct kunit *test) usercopy_env_free(env); } +static char buf_zero(int offset) +{ + return 0; +} + static char buf_pattern(int offset) { return offset & 0xff; @@ -227,6 +232,7 @@ static void assert_size_valid(struct kunit *test, static void assert_src_valid(struct kunit *test, const struct usercopy_params *params, + char (*buf_expected)(int), const char *src, long src_offset, unsigned long ret) { @@ -237,9 +243,10 @@ static void assert_src_valid(struct kunit *test, * A usercopy MUST NOT modify the source buffer. */ for (int i = 0; i < PAGE_SIZE; i++) { + char expected = buf_expected(i); char val = src[i]; - if (val == buf_pattern(i)) + if (val == expected) continue; KUNIT_ASSERT_FAILURE(test, @@ -250,6 +257,7 @@ static void assert_src_valid(struct kunit *test, static void assert_dst_valid(struct kunit *test, const struct usercopy_params *params, + char (*buf_expected)(int), const char *dst, long dst_offset, unsigned long ret) { @@ -260,9 +268,10 @@ static void assert_dst_valid(struct kunit *test, * A usercopy MUST NOT modify any bytes before the destination buffer. */ for (int i = 0; i < dst_offset; i++) { + char expected = buf_expected(i); char val = dst[i]; - if (val == 0) + if (val == expected) continue; KUNIT_ASSERT_FAILURE(test, @@ -275,9 +284,10 @@ static void assert_dst_valid(struct kunit *test, * buffer. */ for (int i = dst_offset + size - ret; i < PAGE_SIZE; i++) { + char expected = buf_expected(i); char val = dst[i]; - if (val == 0) + if (val == expected) continue; KUNIT_ASSERT_FAILURE(test, @@ -313,6 +323,29 @@ static void assert_copy_valid(struct kunit *test, } } +static void assert_clear_valid(struct kunit *test, + const struct usercopy_params *params, + const char *dst, long dst_offset, + unsigned long ret) +{ + const unsigned long size = params->size; + const unsigned long offset = params->offset; + + /* + * Have we actually zeroed the bytes we expected to? + */ + for (int i = 0; i < params->size - ret; i++) { + char dst_val = dst[dst_offset + i]; + + if (dst_val == 0) + continue; + + KUNIT_ASSERT_FAILURE(test, + "zeroed bytes incorrect (dst_page[%ld+%d]=0x%x, offset=%ld, size=%lu, ret=%lu", + dst_offset, i, dst_val, + offset, size, ret); + } +} static unsigned long do_copy_to_user(const struct usercopy_env *env, const struct usercopy_params *params) { @@ -341,6 +374,19 @@ static unsigned long do_copy_from_user(const struct usercopy_env *env, return ret; } +static unsigned long do_clear_user(const struct usercopy_env *env, + const struct usercopy_params *params) +{ + void __user *uptr = (void __user *)UBUF_ADDR_BASE + params->offset; + unsigned long ret; + + kthread_use_mm(env->mm); + ret = clear_user(uptr, params->size); + kthread_unuse_mm(env->mm); + + return ret; +} + /* * Test offsets in the ranges [-size, 0] and [PAGE_SIZE - size, PAGE_SIZE] so * that we check all possible fault boundaries. @@ -368,8 +414,10 @@ static void test_copy_to_user(struct kunit *test) ret = do_copy_to_user(env, ¶ms); assert_size_valid(test, ¶ms, ret); - assert_src_valid(test, ¶ms, env->kbuf, 0, ret); - assert_dst_valid(test, ¶ms, env->ubuf, params.offset, ret); + assert_src_valid(test, ¶ms, buf_pattern, + env->kbuf, 0, ret); + assert_dst_valid(test, ¶ms, buf_zero, + env->ubuf, params.offset, ret); assert_copy_valid(test, ¶ms, env->ubuf, params.offset, env->kbuf, 0, @@ -395,8 +443,10 @@ static void test_copy_from_user(struct kunit *test) ret = do_copy_from_user(env, ¶ms); assert_size_valid(test, ¶ms, ret); - assert_src_valid(test, ¶ms, env->ubuf, params.offset, ret); - assert_dst_valid(test, ¶ms, env->kbuf, 0, ret); + assert_src_valid(test, ¶ms, buf_pattern, + env->ubuf, params.offset, ret); + assert_dst_valid(test, ¶ms, buf_zero, + env->kbuf, 0, ret); assert_copy_valid(test, ¶ms, env->kbuf, 0, env->ubuf, params.offset, @@ -404,6 +454,30 @@ static void test_copy_from_user(struct kunit *test) } } +static void test_clear_user(struct kunit *test) +{ + const struct usercopy_env *env = test->priv; + const unsigned long size = *(unsigned long *)test->param_value; + + for_each_offset(size, offset) { + const struct usercopy_params params = { + .size = size, + .offset = offset, + }; + unsigned long ret; + + buf_init_pattern(env->ubuf); + + ret = do_clear_user(env, ¶ms); + + assert_size_valid(test, ¶ms, ret); + assert_dst_valid(test, ¶ms, buf_pattern, + env->ubuf, params.offset, ret); + assert_clear_valid(test, ¶ms, + env->ubuf, params.offset, + ret); + } +} static const void *gen_size(const void *prev, char *desc) { /* @@ -430,6 +504,7 @@ static const void *gen_size(const void *prev, char *desc) static struct kunit_case usercopy_cases[] = { KUNIT_CASE_PARAM(test_copy_to_user, gen_size), KUNIT_CASE_PARAM(test_copy_from_user, gen_size), + KUNIT_CASE_PARAM(test_clear_user, gen_size), { /* sentinel */ } };