From: Kees Cook <kees@kernel.org>
To: Peter Collingbourne <pcc@google.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
Andrew Morton <akpm@linux-foundation.org>,
Andy Shevchenko <andy@kernel.org>,
Andrey Konovalov <andreyknvl@gmail.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Mark Rutland <mark.rutland@arm.com>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-hardening@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, stable@vger.kernel.org
Subject: Re: [PATCH v2 1/2] string: Add load_unaligned_zeropad() code path to sized_strscpy()
Date: Tue, 18 Mar 2025 20:06:28 -0700 [thread overview]
Message-ID: <202503181957.55A0E0A@keescook> (raw)
In-Reply-To: <20250318214035.481950-2-pcc@google.com>
On Tue, Mar 18, 2025 at 02:40:32PM -0700, Peter Collingbourne wrote:
> The call to read_word_at_a_time() in sized_strscpy() is problematic
> with MTE because it may trigger a tag check fault when reading
> across a tag granule (16 bytes) boundary. To make this code
> MTE compatible, let's start using load_unaligned_zeropad()
> on architectures where it is available (i.e. architectures that
> define CONFIG_DCACHE_WORD_ACCESS). Because load_unaligned_zeropad()
> takes care of page boundaries as well as tag granule boundaries,
> also disable the code preventing crossing page boundaries when using
> load_unaligned_zeropad().
>
> Signed-off-by: Peter Collingbourne <pcc@google.com>
> Link: https://linux-review.googlesource.com/id/If4b22e43b5a4ca49726b4bf98ada827fdf755548
> Fixes: 94ab5b61ee16 ("kasan, arm64: enable CONFIG_KASAN_HW_TAGS")
> Cc: stable@vger.kernel.org
> ---
> v2:
> - new approach
>
> lib/string.c | 13 ++++++++++---
> 1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/lib/string.c b/lib/string.c
> index eb4486ed40d25..b632c71df1a50 100644
> --- a/lib/string.c
> +++ b/lib/string.c
> @@ -119,6 +119,7 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
> if (count == 0 || WARN_ON_ONCE(count > INT_MAX))
> return -E2BIG;
>
> +#ifndef CONFIG_DCACHE_WORD_ACCESS
> #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
I would prefer this were written as:
#if !defined(CONFIG_DCACHE_WORD_ACCESS) && \
defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
Having 2 #ifs makes me think there is some reason for having them
separable. But the logic here is for a single check.
> /*
> * If src is unaligned, don't cross a page boundary,
> @@ -133,12 +134,14 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
> /* If src or dest is unaligned, don't do word-at-a-time. */
> if (((long) dest | (long) src) & (sizeof(long) - 1))
> max = 0;
> +#endif
> #endif
(Then no second #endif needed)
>
> /*
> - * read_word_at_a_time() below may read uninitialized bytes after the
> - * trailing zero and use them in comparisons. Disable this optimization
> - * under KMSAN to prevent false positive reports.
> + * load_unaligned_zeropad() or read_word_at_a_time() below may read
> + * uninitialized bytes after the trailing zero and use them in
> + * comparisons. Disable this optimization under KMSAN to prevent
> + * false positive reports.
> */
> if (IS_ENABLED(CONFIG_KMSAN))
> max = 0;
> @@ -146,7 +149,11 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
> while (max >= sizeof(unsigned long)) {
> unsigned long c, data;
>
> +#ifdef CONFIG_DCACHE_WORD_ACCESS
> + c = load_unaligned_zeropad(src+res);
> +#else
> c = read_word_at_a_time(src+res);
> +#endif
> if (has_zero(c, &data, &constants)) {
> data = prep_zero_mask(c, data, &constants);
> data = create_zero_mask(data);
The rest seems good. Though I do wonder: what happens on a page boundary
for read_word_at_a_time(), then? We get back zero-filled remainder? Will
that hide a missing NUL terminator? As in, it's not actually there
because of the end of the page/granule, but a zero was put in, so now
it looks like it's been terminated and the exception got eaten? And
doesn't this hide MTE faults since we can't differentiate "overran MTE
tag" from "overran granule while over-reading"?
--
Kees Cook
next prev parent reply other threads:[~2025-03-19 3:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-18 21:40 [PATCH v2 0/2] string: Add load_unaligned_zeropad() code path to sized_strscpy() Peter Collingbourne
2025-03-18 21:40 ` [PATCH v2 1/2] " Peter Collingbourne
2025-03-19 3:06 ` Kees Cook [this message]
2025-03-19 3:35 ` Peter Collingbourne
2025-03-18 21:40 ` [PATCH v2 2/2] kasan: Add strscpy() test to trigger tag fault on arm64 Peter Collingbourne
2025-03-20 17:25 ` Andrey Konovalov
2025-03-21 2:41 ` Peter Collingbourne
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=202503181957.55A0E0A@keescook \
--to=kees@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=andreyknvl@gmail.com \
--cc=andy@kernel.org \
--cc=brauner@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=jack@suse.cz \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=pcc@google.com \
--cc=stable@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/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