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
next prev parent 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