[PATCH v2 37/63] string.h: Introduce memset_after() for wiping trailing members/padding
From: Kees Cook <hidden>
Date: 2021-08-18 06:11:49
Also in:
dri-devel, linux-block, linux-hardening, linux-kbuild, linux-wireless, lkml, netdev
Subsystem:
generic string library, library code, the rest · Maintainers:
Kees Cook, Andrew Morton, Linus Torvalds
A common idiom in kernel code is to wipe the contents of a structure after a given member. This is especially useful in places where there is trailing padding. These open-coded cases are usually difficult to read and very sensitive to struct layout changes. Introduce a new helper, memset_after() that takes the target struct instance, the byte to write, and the member name after which the zeroing should start. Additionally adds memset_startat() for wiping trailing members _starting_ at a specific member instead of after a member, which is more readable in certain circumstances, but doesn't include any preceding padding. Cc: Steffen Klassert <steffen.klassert@secunet.com> Cc: Herbert Xu <herbert@gondor.apana.org.au> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Francis Laniel <redacted> Cc: Vincenzo Frascino <vincenzo.frascino@arm.com> Cc: Daniel Axtens <redacted> Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook <redacted> --- include/linux/string.h | 29 +++++++++++++++++++++++++++++ lib/test_memcpy.c | 24 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+)
diff --git a/include/linux/string.h b/include/linux/string.h
index cbe889e404e2..fe56a1774207 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h@@ -272,6 +272,35 @@ static __always_inline void memcpy_and_pad(void *dest, size_t dest_len, memcpy(dest, src, dest_len); } +/** + * memset_after - Set a value after a struct member to the end of a struct + * + * @obj: Address of target struct instance + * @v: Byte value to repeatedly write + * @member: after which struct member to start writing bytes + * + * This is good for clearing padding following the given member. + */ +#define memset_after(obj, v, member) do { \ + memset((u8 *)(obj) + offsetofend(typeof(*(obj)), member), v, \ + sizeof(*(obj)) - offsetofend(typeof(*(obj)), member)); \ +} while (0) + +/** + * memset_startat - Set a value starting at a member to the end of a struct + * + * @obj: Address of target struct instance + * @v: Byte value to repeatedly write + * @member: struct member to start writing at + * + * Note that if there is padding between the prior member and the target + * member, memset_after() should be used to clear the prior padding. + */ +#define memset_startat(obj, v, member) do { \ + memset((u8 *)(obj) + offsetof(typeof(*(obj)), member), v, \ + sizeof(*(obj)) - offsetof(typeof(*(obj)), member)); \ +} while (0) + /** * str_has_prefix - Test if a string has a given prefix * @str: The string to test
diff --git a/lib/test_memcpy.c b/lib/test_memcpy.c
index be192b8e82b7..50bc99552a17 100644
--- a/lib/test_memcpy.c
+++ b/lib/test_memcpy.c@@ -215,6 +215,20 @@ static void memset_test(struct kunit *test) 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, }, }; + struct some_bytes after = { + .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + }, + }; + struct some_bytes startat = { + .data = { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + }, + }; struct some_bytes dest = { }; int count, value; u8 *ptr;
@@ -245,6 +259,16 @@ static void memset_test(struct kunit *test) ptr += 8; memset(ptr++, value++, count++); compare("argument side-effects", dest, three); + + /* Verify memset_after() */ + dest = control; + memset_after(&dest, 0x72, three); + compare("memset_after()", dest, after); + + /* Verify memset_startat() */ + dest = control; + memset_startat(&dest, 0x79, four); + compare("memset_startat()", dest, startat); #undef TEST_OP }
--
2.30.2