public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Justin Stitt <justinstitt@google.com>
Cc: Charles Bertsch <cbertsch@cox.net>,
	linux-scsi@vger.kernel.org, MPT-FusionLinux.pdl@broadcom.com
Subject: Re: startup BUG at lib/string_helpers.c from scsi fusion mptsas
Date: Mon, 8 Apr 2024 16:19:00 -0700	[thread overview]
Message-ID: <202404081602.1B1773256@keescook> (raw)
In-Reply-To: <CAFhGd8p=R4P6J9KoMGcXij=fN=9sixVzjuz95TLKP1TexnvV8Q@mail.gmail.com>

On Mon, Apr 08, 2024 at 12:59:52PM -0700, Justin Stitt wrote:
> https://lore.kernel.org/all/d45631ac-3ee6-45cc-8b5a-fab130ce39d7@cox.net/
> 
> On Sat, Apr 6, 2024 at 1:42 PM Charles Bertsch <cbertsch@cox.net> wrote:
> > Justin,
> > Yes, undo of that patch does fix the problem, the scsi controller and
> > all disks are visible.
> >
> > So did changing .config so that FORTIFY would not be used.
> >
> > Given other messages on this subject, there seems a basic conflict
> > between using strscpy() to mean -- copy however much will fit, and leave
> > a proper NUL-terminated string, versus FORTIFY trying to signal that
> > something has been lost. Is there a strscpy variation (_pad maybe?) that
> > FORTIFY will remain calm about truncation?
> 
> I think fortified strscpy() should allow for the truncation, this, at
> least in my eyes, is the expected behavior of strscpy(). You copy as
> much as you can from the source and slap a '\0' to the end without
> overflowing the destination.

The trouble is with truncation. Some strings:

char longstr[]  = "This is long."; // sizeof(longstr) == 14, strlen(longstr) == 13
char truncstr[] = "This is trunc";
char nonstr[sizeof(truncstr) - 1]; // sizeof(nonstr) == 13
char dest[64];

/* Create "nonstr" without a trailing NUL */
memcpy(nonstr, trunc, strlen(trunc));

strscpy(dest, long, sizeof(dest) /* 64 */)
	=> 13 (i.e. strlen(longstr))
strscpy(dest, long, strlen(longstr) + 1 /* 14 */)
	=> 13
strscpy(dest, long, strlen(longstr) /* 13 */)
	=> -E2BIG, we saw that "." wasn't a NUL
strscpy(dest, nonstr, 13)
	=> -E2BIG, we saw that "." wasn't a NUL
strscpy(dest, nonstr, 14)
	=> fortify error, we can't examine the char after "."

If we used sizeof(src) to try to work around the off-by-one, we'd
suddenly lose the ability to detect actually corrupt strings, because
we'd silently start "accepting" strings that were exactly sized off by
1, and gain ambiguity in our handling.

> I think Kees has some plans to address this as we spoke offline.

But, this use of strncpy() *is* another "legitimate" use-case. But like
the other strncpy() uses, it is ambiguous. So, I think we need the
reverse of strtomem(), to take a "maybe NUL padded but not terminated"
character array and unambiguously construct a NUL-terminated string from
it.

I think something like this, memtostr:

diff --git a/include/linux/string.h b/include/linux/string.h
index 9ba8b4597009..5def02c7c0ce 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -422,6 +422,30 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
 	memcpy(dest, src, strnlen(src, min(_src_len, _dest_len)));	\
 } while (0)
 
+/**
+ * memtostr - Copy a possibly non-NUL-term string to a NUL-term string
+ * @dest: Pointer to destination NUL-terminates string
+ * @src: Pointer to character array (likely marked as __nonstring)
+ *
+ * This is a replacement for strncpy() uses where the source is not
+ * a NUL-terminated string.
+ *
+ * Note that sizes of @dest and @src must be known at compile-time.
+ */
+#define memtostr(dest, src)	do {					\
+	const size_t _dest_len = __builtin_object_size(dest, 1);	\
+	const size_t _src_len = __builtin_object_size(src, 1);		\
+	const size_t _src_chars = strnlen(src, _src_len);		\
+	const size_t _copy_len = min(_dest_len - 1, _src_chars);	\
+									\
+	BUILD_BUG_ON(!__builtin_constant_p(_dest_len) ||		\
+		     !__builtin_constant_p(_src_len) ||			\
+		     _dest_len == 0 || _dest_len == (size_t)-1 ||	\
+		     _src_len == 0 || _src_len == (size_t)-1);		\
+	memcpy(dest, src, _copy_len);					\
+	dest[_copy_len] = '\0';						\
+} while (0)
+
 /**
  * memset_after - Set a value after a struct member to the end of a struct
  *


I've also identified other cases where this pattern exists, so I think
we can apply this and any needed fixes using it instead of strscpy().

-- 
Kees Cook

  reply	other threads:[~2024-04-08 23:19 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-01 22:43 startup BUG at lib/string_helpers.c from scsi fusion mptsas Charles Bertsch
2024-04-03 23:20 ` Bart Van Assche
2024-04-04 19:58   ` Justin Stitt
2024-04-04 21:38 ` Justin Stitt
2024-04-04 21:53   ` James Bottomley
2024-04-04 22:04     ` Justin Stitt
2024-04-04 22:29       ` James Bottomley
2024-04-04 22:33         ` James Bottomley
2024-04-04 22:43           ` Martin K. Petersen
2024-04-04 22:47           ` Justin Stitt
2024-04-04 23:39             ` Kees Cook
2024-04-05  0:10               ` Justin Stitt
2024-04-05  0:12                 ` Justin Stitt
2024-04-04 23:57           ` Kees Cook
2024-04-06 20:42   ` Charles Bertsch
2024-04-08 19:59     ` Justin Stitt
2024-04-08 23:19       ` Kees Cook [this message]
2024-04-10 20:51         ` Justin Stitt
2024-04-10 21:14           ` Charles Bertsch

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=202404081602.1B1773256@keescook \
    --to=keescook@chromium.org \
    --cc=MPT-FusionLinux.pdl@broadcom.com \
    --cc=cbertsch@cox.net \
    --cc=justinstitt@google.com \
    --cc=linux-scsi@vger.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