From: Catalin Marinas <catalin.marinas@arm.com>
To: Mark Rutland <mark.rutland@arm.com>
Cc: linux-kernel@vger.kernel.org, agordeev@linux.ibm.com,
aou@eecs.berkeley.edu, bp@alien8.de, dave.hansen@linux.intel.com,
davem@davemloft.net, gor@linux.ibm.com, hca@linux.ibm.com,
linux-arch@vger.kernel.org, linux@armlinux.org.uk,
mingo@redhat.com, palmer@dabbelt.com, paul.walmsley@sifive.com,
robin.murphy@arm.com, tglx@linutronix.de,
torvalds@linux-foundation.org, viro@zeniv.linux.org.uk,
will@kernel.org
Subject: Re: [PATCH v2 1/4] lib: test copy_{to,from}_user()
Date: Tue, 21 Mar 2023 17:09:48 +0000 [thread overview]
Message-ID: <ZBnk3O0QLs6+8KNN@arm.com> (raw)
In-Reply-To: <20230321122514.1743889-2-mark.rutland@arm.com>
On Tue, Mar 21, 2023 at 12:25:11PM +0000, Mark Rutland wrote:
> +static void assert_size_valid(struct kunit *test,
> + const struct usercopy_params *params,
> + unsigned long ret)
> +{
> + const unsigned long size = params->size;
> + const unsigned long offset = params->offset;
I think you should drop the 'unsigned' for 'offset', it better matches
the usercopy_params structure and the 'offset < 0' test.
> +
> + if (ret > size) {
> + KUNIT_ASSERT_FAILURE(test,
> + "return value is impossibly large (offset=%ld, size=%lu, ret=%lu)",
> + offset, size, ret);
> + }
> +
> + /*
> + * When the user buffer starts within a faulting page, no bytes can be
> + * copied, so ret must equal size.
> + */
> + if (offset < 0 || offset >= PAGE_SIZE) {
> + if (ret == size)
> + return;
> +
> + KUNIT_ASSERT_FAILURE(test,
> + "impossible copy did not fail (offset=%ld, size=%lu, ret=%lu)",
> + offset, size, ret);
> + }
> +
> + /*
> + * When the user buffer fits entirely within a non-faulting page, all
> + * bytes must be copied, so ret must equal 0.
> + */
> + if (offset + size <= PAGE_SIZE) {
> + if (ret == 0)
> + return;
> +
> + KUNIT_ASSERT_FAILURE(test,
> + "completely possible copy failed (offset=%ld, size=%lu, ret=%lu)",
> + offset, size, ret);
> + }
> +
> + /*
> + * The buffer starts in a non-faulting page, but continues into a
> + * faulting page. At least one byte must be copied, and at most all the
> + * non-faulting bytes may be copied.
> + */
> + if (ret == size) {
> + KUNIT_ASSERT_FAILURE(test,
> + "too few bytes consumed (offset=%ld, size=%lu, ret=%lu)",
> + offset, size, ret);
> + }
> +
> + if (ret < (offset + size) - PAGE_SIZE) {
> + KUNIT_ASSERT_FAILURE(test,
> + "too many bytes consumed (offset=%ld, size=%lu, ret=%lu)",
> + offset, size, ret);
> + }
> +}
Nitpick: slightly less indentation probably if we write the above as:
KUNIT_ASSERT_TRUE_MSG(test,
ret < (offset + size) - PAGE_SIZE,
"too many bytes consumed (offset=%ld, size=%lu, ret=%lu)",
offset, size, ret);
Not sure this works for the early return cases above.
> +static void assert_src_valid(struct kunit *test,
> + const struct usercopy_params *params,
> + const char *src, long src_offset,
> + unsigned long ret)
> +{
> + const unsigned long size = params->size;
> + const unsigned long offset = params->offset;
The unsigned offset here doesn't matter much but I'd drop it as well.
> + /*
> + * A usercopy MUST NOT modify the source buffer.
> + */
> + for (int i = 0; i < PAGE_SIZE; i++) {
> + char val = src[i];
> +
> + if (val == buf_pattern(i))
> + continue;
> +
> + KUNIT_ASSERT_FAILURE(test,
> + "source bytes modified (src[%d]=0x%x, offset=%ld, size=%lu, ret=%lu)",
> + i, offset, size, ret);
> + }
> +}
> +
> +static void assert_dst_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;
> +
> + /*
> + * A usercopy MUST NOT modify any bytes before the destination buffer.
> + */
> + for (int i = 0; i < dst_offset; i++) {
> + char val = dst[i];
> +
> + if (val == 0)
> + continue;
> +
> + KUNIT_ASSERT_FAILURE(test,
> + "pre-destination bytes modified (dst_page[%d]=0x%x, offset=%ld, size=%lu, ret=%lu)",
> + i, val, offset, size, ret);
> + }
> +
> + /*
> + * A usercopy MUST NOT modify any bytes after the end of the destination
> + * buffer.
> + */
Without looking at the arm64 fixes in this series, I think such test can
fail for us currently given the right offset.
> + for (int i = dst_offset + size - ret; i < PAGE_SIZE; i++) {
> + char val = dst[i];
> +
> + if (val == 0)
> + continue;
> +
> + KUNIT_ASSERT_FAILURE(test,
> + "post-destination bytes modified (dst_page[%d]=0x%x, offset=%ld, size=%lu, ret=%lu)",
> + i, val, offset, size, ret);
> + }
> +}
[...]
> +/*
> + * Generate the size and offset combinations to test.
> + *
> + * Usercopies may have different paths for small/medium/large copies, but
> + * typically max out at 64 byte chunks. We test sizes 0 to 128 bytes to check
> + * all combinations of leading/trailing chunks and bulk copies.
> + *
> + * For each size, we test every offset relative to a leading and trailing page
> + * boundary (i.e. [size, 0] and [PAGE_SIZE - size, PAGE_SIZE]) to check every
> + * possible faulting boundary.
> + */
> +#define for_each_size_offset(size, offset) \
> + for (unsigned long size = 0; size <= 128; size++) \
> + for (long offset = -size; \
> + offset <= (long)PAGE_SIZE; \
> + offset = (offset ? offset + 1: (PAGE_SIZE - size)))
> +
> +static void test_copy_to_user(struct kunit *test)
> +{
> + const struct usercopy_env *env = test->priv;
> +
> + for_each_size_offset(size, offset) {
> + const struct usercopy_params params = {
> + .size = size,
> + .offset = offset,
> + };
> + unsigned long ret;
> +
> + buf_init_zero(env->ubuf);
> + buf_init_pattern(env->kbuf);
> +
> + 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_copy_valid(test, ¶ms,
> + env->ubuf, params.offset,
> + env->kbuf, 0,
> + ret);
> + }
> +}
IIUC, in such tests you only vary the destination offset. Our copy
routines in general try to align the source and leave the destination
unaligned for performance. It would be interesting to add some variation
on the source offset as well to spot potential issues with that part of
the memcpy routines.
--
Catalin
next prev parent reply other threads:[~2023-03-21 17:10 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-21 12:25 [PATCH v2 0/4] usercopy: generic tests + arm64 fixes Mark Rutland
2023-03-21 12:25 ` [PATCH v2 1/4] lib: test copy_{to,from}_user() Mark Rutland
2023-03-21 17:09 ` Catalin Marinas [this message]
2023-03-22 14:05 ` Mark Rutland
2023-03-23 22:16 ` David Laight
2023-03-27 10:11 ` Catalin Marinas
2023-03-21 18:04 ` Linus Torvalds
2023-03-22 13:55 ` Mark Rutland
2023-03-21 12:25 ` [PATCH v2 2/4] lib: test clear_user() Mark Rutland
2023-03-21 17:13 ` Catalin Marinas
2023-03-21 12:25 ` [PATCH v2 3/4] arm64: fix __raw_copy_to_user semantics Mark Rutland
2023-03-21 17:50 ` Linus Torvalds
2023-03-22 14:16 ` Mark Rutland
2023-03-22 14:48 ` Catalin Marinas
2023-03-22 15:30 ` Mark Rutland
2023-03-22 16:39 ` Linus Torvalds
2023-03-21 12:25 ` [PATCH v2 4/4] arm64: fix clear_user() semantics Mark Rutland
2023-11-15 22:30 ` [PATCH v2 0/4] usercopy: generic tests + arm64 fixes Kees Cook
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZBnk3O0QLs6+8KNN@arm.com \
--to=catalin.marinas@arm.com \
--cc=agordeev@linux.ibm.com \
--cc=aou@eecs.berkeley.edu \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=davem@davemloft.net \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=linux-arch@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=robin.murphy@arm.com \
--cc=tglx@linutronix.de \
--cc=torvalds@linux-foundation.org \
--cc=viro@zeniv.linux.org.uk \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).