All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: David Gow <davidgow@google.com>
Cc: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Rasmus Villemoes <linux@rasmusvillemoes.dk>,
	Daniel Micay <danielmicay@gmail.com>,
	Francis Laniel <laniel_francis@privacyrequired.com>,
	Bart Van Assche <bvanassche@acm.org>,
	Linux Memory Management List <linux-mm@kvack.org>,
	clang-built-linux@googlegroups.com,
	linux-hardening@vger.kernel.org
Subject: Re: [PATCH for-next 21/25] lib: Introduce CONFIG_TEST_MEMCPY
Date: Tue, 24 Aug 2021 19:32:51 -0700	[thread overview]
Message-ID: <202108241921.26866A8F@keescook> (raw)
In-Reply-To: <CABVgOSn=fmUctW_xexDyS_c4G3ee4vGvuJLaanRDQbzQkfAOBQ@mail.gmail.com>

On Tue, Aug 24, 2021 at 03:00:19PM +0800, David Gow wrote:
> On Sun, Aug 22, 2021 at 3:56 PM Kees Cook <keescook@chromium.org> wrote:
> >
> > Before changing anything about memcpy(), memmove(), and memset(), add
> > run-time tests to check basic behaviors for any regressions.
> >
> > Signed-off-by: Kees Cook <keescook@chromium.org>
> > ---
> 
> Thanks for adding a KUnit test here: it's great to have better
> coverage of some of these basic functions!
> 
> There's a name mismatch with the Kconfig entry and the Makefile,
> otherwise this looks good and works fine on my machine (under both UML
> and qemu/x86_64).

Hah! Whoops. Thanks for noticing this -- I think I didn't use a clean
tree and never noticed because I had the old module.

> It would be possible to split these tests up further if you wanted,
> which could be useful if there's a desire to track the individual
> assertion results independently. That's probably what I'd've done, but
> It's a matter of personal preference either way, though: the tests
> aren't absurdly huge or over-complicated as-is.

Noted. Yeah, for me, I think it's "does memcpy work or not?" and each of
the EXPECTs are required, so it felt like 1 test with lots of EXPECTs.

> > +FORTIFY_SOURCE
> > +M:     Kees Cook <keescook@chomium.org>
> > +L:     linux-hardening@vger.kernel.org
> > +S:     Supported
> > +F:     include/linux/fortify-string.h
> > +F:     lib/test_fortify/*
> > +F:     scripts/test_fortify.sh
> > +K:     \b__NO_FORTIFY\b
> > +
> 
> Do you want this to be part of the memcpy() KUnit test commit, or is
> it better suited in one of the changes to the actual fortify stuff?

Whoops, thanks. This got --fixup'ed into the wrong patch. I've moved it
now.

> >  FPGA DFL DRIVERS
> >  M:     Wu Hao <hao.wu@intel.com>
> >  R:     Tom Rix <trix@redhat.com>
> > diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> > index 831212722924..9199be57ba2a 100644
> > --- a/lib/Kconfig.debug
> > +++ b/lib/Kconfig.debug
> > @@ -2467,6 +2467,17 @@ config RATIONAL_KUNIT_TEST
> >
> >           If unsure, say N.
> >
> > +config MEMCPY_KUNIT_TEST
> > +       tristate "Test memcpy(), memmove(), and memset() functions at runtime" if !KUNIT_ALL_TESTS
> > +       depends on KUNIT
> > +       default KUNIT_ALL_TESTS
> > +       help
> > +         Builds unit tests for memcpy(), memmove(), and memset() functions.
> > +         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 bd17c2bf43e1..8a4c8bdb38a2 100644
> > --- a/lib/Makefile
> > +++ b/lib/Makefile
> > @@ -77,6 +77,7 @@ obj-$(CONFIG_TEST_MIN_HEAP) += test_min_heap.o
> >  obj-$(CONFIG_TEST_LKM) += test_module.o
> >  obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
> >  obj-$(CONFIG_TEST_OVERFLOW) += test_overflow.o
> > +obj-$(CONFIG_TEST_MEMCPY) += test_memcpy.o
> 
> This doesn't match CONFIG_MEMCPY_KUNIT_TEST above, so the test is
> never compiled in.

Now fixed to be CONFIG_MEMCPY_KUNIT_TEST.

> 
> >  obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
> >  obj-$(CONFIG_TEST_SORT) += test_sort.o
> >  obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
> > diff --git a/lib/test_memcpy.c b/lib/test_memcpy.c
> > new file mode 100644
> > index 000000000000..ec546cec883e
> > --- /dev/null
> > +++ b/lib/test_memcpy.c
> > @@ -0,0 +1,265 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Test cases for memcpy(), memmove(), and memset().
> > + */
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <kunit/test.h>
> > +#include <linux/device.h>
> > +#include <linux/init.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mm.h>
> > +#include <linux/module.h>
> > +#include <linux/overflow.h>
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +#include <linux/vmalloc.h>
> > +
> > +struct some_bytes {
> > +       union {
> > +               u8 data[32];
> > +               struct {
> > +                       u32 one;
> > +                       u16 two;
> > +                       u8  three;
> > +                       /* 1 byte hole */
> > +                       u32 four[4];
> > +               };
> > +       };
> > +};
> > +
> > +#define check(instance, v) do {        \
> > +       int i;  \
> > +       BUILD_BUG_ON(sizeof(instance.data) != 32);      \
> > +       for (i = 0; i < sizeof(instance.data); i++) {   \
> > +               KUNIT_ASSERT_EQ_MSG(test, instance.data[i], v, \
> > +                       "line %d: '%s' not initialized to 0x%02x @ %d (saw 0x%02x)\n", \
> > +                       __LINE__, #instance, v, i, instance.data[i]);   \
> > +       }       \
> > +} while (0)
> > +
> > +#define compare(name, one, two) do { \
> > +       int i; \
> > +       BUILD_BUG_ON(sizeof(one) != sizeof(two)); \
> > +       for (i = 0; i < sizeof(one); i++) {     \
> > +               KUNIT_EXPECT_EQ_MSG(test, one.data[i], two.data[i], \
> > +                       "line %d: %s.data[%d] (0x%02x) != %s.data[%d] (0x%02x)\n", \
> > +                       __LINE__, #one, i, one.data[i], #two, i, two.data[i]); \
> > +       }       \
> > +       kunit_info(test, "ok: " TEST_OP "() " name "\n");       \
> > +} while (0)
> > +
> > +static void memcpy_test(struct kunit *test)
> > +{
> > +#define TEST_OP "memcpy"
> > +       struct some_bytes control = {
> > +               .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                       },
> > +       };
> > +       struct some_bytes zero = { };
> > +       struct some_bytes middle = {
> > +               .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
> > +                         0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                       },
> > +       };
> > +       struct some_bytes three = {
> > +               .data = { 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
> > +                       },
> > +       };
> > +       struct some_bytes dest = { };
> > +       int count;
> > +       u8 *ptr;
> > +
> > +       /* Verify static initializers. */
> > +       check(control, 0x20);
> > +       check(zero, 0);
> > +       compare("static initializers", dest, zero);
> > +
> > +       /* Verify assignment. */
> > +       dest = control;
> > +       compare("direct assignment", dest, control);
> > +
> > +       /* Verify complete overwrite. */
> > +       memcpy(dest.data, zero.data, sizeof(dest.data));
> > +       compare("complete overwrite", dest, zero);
> > +
> > +       /* Verify middle overwrite. */
> > +       dest = control;
> > +       memcpy(dest.data + 12, zero.data, 7);
> > +       compare("middle overwrite", dest, middle);
> > +
> > +       /* Verify argument side-effects aren't repeated. */
> > +       dest = control;
> > +       ptr = dest.data;
> > +       count = 1;
> > +       memcpy(ptr++, zero.data, count++);
> > +       ptr += 8;
> > +       memcpy(ptr++, zero.data, count++);
> > +       compare("argument side-effects", dest, three);
> > +#undef TEST_OP
> > +}
> > +
> > +static void memmove_test(struct kunit *test)
> > +{
> > +#define TEST_OP "memmove"
> > +       struct some_bytes control = {
> > +               .data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                       },
> > +       };
> > +       struct some_bytes zero = { };
> > +       struct some_bytes middle = {
> > +               .data = { 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00,
> > +                         0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                       },
> > +       };
> > +       struct some_bytes five = {
> > +               .data = { 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                       },
> > +       };
> > +       struct some_bytes overlap = {
> > +               .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> > +                         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                       },
> > +       };
> > +       struct some_bytes overlap_expected = {
> > +               .data = { 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x07,
> > +                         0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                         0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
> > +                       },
> > +       };
> > +       struct some_bytes dest = { };
> > +       int count;
> > +       u8 *ptr;
> > +
> > +       /* Verify static initializers. */
> > +       check(control, 0x99);
> > +       check(zero, 0);
> > +       compare("static initializers", zero, dest);
> > +
> > +       /* Verify assignment. */
> > +       dest = control;
> > +       compare("direct assignment", dest, control);
> > +
> > +       /* Verify complete overwrite. */
> > +       memmove(dest.data, zero.data, sizeof(dest.data));
> > +       compare("complete overwrite", dest, zero);
> > +
> > +       /* Verify middle overwrite. */
> > +       dest = control;
> > +       memmove(dest.data + 12, zero.data, 7);
> > +       compare("middle overwrite", dest, middle);
> > +
> > +       /* Verify argument side-effects aren't repeated. */
> > +       dest = control;
> > +       ptr = dest.data;
> > +       count = 2;
> > +       memmove(ptr++, zero.data, count++);
> > +       ptr += 9;
> > +       memmove(ptr++, zero.data, count++);
> > +       compare("argument side-effects", dest, five);
> > +
> > +       /* Verify overlapping overwrite is correct. */
> > +       ptr = &overlap.data[2];
> > +       memmove(ptr, overlap.data, 5);
> > +       compare("overlapping write", overlap, overlap_expected);
> > +#undef TEST_OP
> > +}
> > +
> > +static void memset_test(struct kunit *test)
> > +{
> > +#define TEST_OP "memset"
> > +       struct some_bytes control = {
> > +               .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                       },
> > +       };
> > +       struct some_bytes complete = {
> > +               .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> > +                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> > +                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> > +                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
> > +                       },
> > +       };
> > +       struct some_bytes middle = {
> > +               .data = { 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
> > +                         0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
> > +                         0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                       },
> > +       };
> > +       struct some_bytes three = {
> > +               .data = { 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x61, 0x61, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
> > +                       },
> > +       };
> > +       struct some_bytes dest = { };
> > +       int count, value;
> > +       u8 *ptr;
> > +
> > +       /* Verify static initializers. */
> > +       check(control, 0x30);
> > +       check(dest, 0);
> > +
> > +       /* Verify assignment. */
> > +       dest = control;
> > +       compare("direct assignment", dest, control);
> > +
> > +       /* Verify complete overwrite. */
> > +       memset(dest.data, 0xff, sizeof(dest.data));
> > +       compare("complete overwrite", dest, complete);
> > +
> > +       /* Verify middle overwrite. */
> > +       dest = control;
> > +       memset(dest.data + 4, 0x31, 16);
> > +       compare("middle overwrite", dest, middle);
> > +
> > +       /* Verify argument side-effects aren't repeated. */
> > +       dest = control;
> > +       ptr = dest.data;
> > +       value = 0x60;
> > +       count = 1;
> > +       memset(ptr++, value++, count++);
> > +       ptr += 8;
> > +       memset(ptr++, value++, count++);
> > +       compare("argument side-effects", dest, three);
> > +#undef TEST_OP
> > +}
> > +
> > +static struct kunit_case memcpy_test_cases[] = {
> > +       KUNIT_CASE(memset_test),
> > +       KUNIT_CASE(memcpy_test),
> > +       KUNIT_CASE(memmove_test),
> > +       {}
> > +};
> > +
> > +static struct kunit_suite memcpy_test_suite = {
> > +       .name = "memcpy-test",
> 
> It may be better to just name the suite "memcpy", since -- by
> definition -- it's a test if it's a KUnit test suite.

Sounds good. I will adjust this.

> 
> > +       .test_cases = memcpy_test_cases,
> > +};
> > +
> > +kunit_test_suite(memcpy_test_suite);
> > +
> > +MODULE_LICENSE("GPL");
> > --
> > 2.30.2
> >

Thanks!

-Kees

-- 
Kees Cook

  reply	other threads:[~2021-08-25  2:32 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-22  7:50 [PATCH for-next 00/25] Prepare for better FORTIFY_SOURCE Kees Cook
2021-08-22  7:50 ` [PATCH for-next 01/25] scsi: ibmvscsi: Avoid multi-field memset() overflow by aiming at srp Kees Cook
2021-08-22  7:50   ` Kees Cook
2021-08-22  7:50 ` [PATCH for-next 02/25] powerpc: Split memset() to avoid multi-field overflow Kees Cook
2021-08-22  7:50   ` Kees Cook
2021-08-22  7:51 ` [PATCH for-next 03/25] stddef: Fix kerndoc for sizeof_field() and offsetofend() Kees Cook
2021-08-22  7:51 ` [PATCH for-next 04/25] stddef: Introduce struct_group() helper macro Kees Cook
2021-08-22  7:51 ` [PATCH for-next 05/25] cxl/core: Replace unions with struct_group() Kees Cook
2021-08-22  7:51 ` [PATCH for-next 06/25] bnxt_en: Use struct_group_attr() for memcpy() region Kees Cook
2021-08-22  7:51 ` [PATCH for-next 07/25] iommu/amd: Use struct_group() " Kees Cook
2021-08-22  7:51 ` [PATCH for-next 08/25] drm/mga/mga_ioc32: " Kees Cook
2021-08-22  7:51 ` [PATCH for-next 09/25] HID: cp2112: " Kees Cook
2021-08-22  7:51 ` [PATCH for-next 10/25] HID: roccat: Use struct_group() to zero kone_mouse_event Kees Cook
2021-08-22  7:51 ` [PATCH for-next 11/25] can: flexcan: Use struct_group() to zero struct flexcan_regs regions Kees Cook
2021-08-22  7:51 ` [PATCH for-next 12/25] cm4000_cs: Use struct_group() to zero struct cm4000_dev region Kees Cook
2021-08-22  7:51 ` [PATCH for-next 13/25] compiler_types.h: Remove __compiletime_object_size() Kees Cook
2021-08-23  6:43   ` Rasmus Villemoes
2021-08-25 19:43     ` Nick Desaulniers
2021-08-25 19:43       ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 14/25] lib/string: Move helper functions out of string.c Kees Cook
2021-08-25 21:48   ` Nick Desaulniers
2021-08-25 21:48     ` Nick Desaulniers
2021-08-26  2:47     ` Kees Cook
2021-08-26 18:08       ` Nick Desaulniers
2021-08-26 18:08         ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 15/25] fortify: Move remaining fortify helpers into fortify-string.h Kees Cook
2021-08-25 21:59   ` Nick Desaulniers
2021-08-25 21:59     ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 16/25] fortify: Explicitly disable Clang support Kees Cook
2021-08-25 19:41   ` Nick Desaulniers
2021-08-25 19:41     ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 17/25] fortify: Fix dropped strcpy() compile-time write overflow check Kees Cook
2021-08-25 21:55   ` Nick Desaulniers
2021-08-25 21:55     ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 18/25] fortify: Prepare to improve strnlen() and strlen() warnings Kees Cook
2021-08-25 22:01   ` Nick Desaulniers
2021-08-25 22:01     ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 19/25] fortify: Allow strlen() and strnlen() to pass compile-time known lengths Kees Cook
2021-08-25 22:05   ` Nick Desaulniers
2021-08-25 22:05     ` Nick Desaulniers
2021-08-26  2:56     ` Kees Cook
2021-08-26 18:02       ` Nick Desaulniers
2021-08-26 18:02         ` Nick Desaulniers
2021-08-22  7:51 ` [PATCH for-next 20/25] fortify: Add compile-time FORTIFY_SOURCE tests Kees Cook
2021-08-22  7:51 ` [PATCH for-next 21/25] lib: Introduce CONFIG_TEST_MEMCPY Kees Cook
2021-08-24  7:00   ` David Gow
2021-08-24  7:00     ` David Gow
2021-08-25  2:32     ` Kees Cook [this message]
2021-10-18 15:46   ` Arnd Bergmann
2021-10-18 19:28     ` Kees Cook
2021-08-22  7:51 ` [PATCH for-next 22/25] string.h: Introduce memset_after() for wiping trailing members/padding Kees Cook
2021-08-22  7:51 ` [PATCH for-next 23/25] xfrm: Use memset_after() to clear padding Kees Cook
2021-08-22  7:51 ` [PATCH for-next 24/25] string.h: Introduce memset_startat() for wiping trailing members and padding Kees Cook
2021-08-22  7:51 ` [PATCH for-next 25/25] btrfs: Use memset_startat() to clear end of struct 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=202108241921.26866A8F@keescook \
    --to=keescook@chromium.org \
    --cc=bvanassche@acm.org \
    --cc=clang-built-linux@googlegroups.com \
    --cc=danielmicay@gmail.com \
    --cc=davidgow@google.com \
    --cc=laniel_francis@privacyrequired.com \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux@rasmusvillemoes.dk \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.