From: Kees Cook <keescook@chromium.org>
To: Justin Stitt <justinstitt@google.com>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>,
linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-hardening@vger.kernel.org
Subject: Re: [PATCH] PNP: replace deprecated strncpy with memcpy
Date: Thu, 19 Oct 2023 17:04:17 -0700 [thread overview]
Message-ID: <202310191637.9EBDCC364@keescook> (raw)
In-Reply-To: <20231019-strncpy-drivers-pnp-pnpbios-rsparser-c-v1-1-e47d93b52e3e@google.com>
On Thu, Oct 19, 2023 at 11:28:32PM +0000, Justin Stitt wrote:
> strncpy() is deprecated for use on NUL-terminated destination strings
> [1] and as such we should prefer more robust and less ambiguous
> interfaces.
>
> After having precisely calculated the lengths and ensuring we don't
> overflow the buffer, this really decays to just a memcpy. Let's not use
> a C string api as it makes the intention of the code confusing.
This is another case where we're building a C string from a byte array.
> It'd be nice to use strscpy() in this case (as we clearly want
> NUL-termination) because it'd clean up the code a bit. However, I don't
> quite know enough about what is going on here to justify a drop-in
> replacement -- too much bit magic and why (PNP_NAME_LEN - 2)? I'm afraid
> using strscpy() may result in copying too many or too few bytes into our
> dev->name buffer resulting in different behavior. At least using
> memcpy() we can ensure the behavior is exactly the same.
>
> Side note:
> NUL-padding is not required because insert_device() calls
> pnpbios_parse_data_stream() with a zero-allocated `dev`:
> 299 | static int __init insert_device(struct pnp_bios_node *node) {
> ...
> 312 | dev = pnp_alloc_dev(&pnpbios_protocol, node->handle, id);
> ...
> 316 | pnpbios_parse_data_stream(dev, node);
>
> then pnpbios_parse_data_stream() calls pnpbios_parse_compatible_ids().
>
> Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1]
> Link: https://github.com/KSPP/linux/issues/90
> Cc: linux-hardening@vger.kernel.org
> Signed-off-by: Justin Stitt <justinstitt@google.com>
tl;dr:
Reviewed-by: Kees Cook <keescook@chromium.org>
My ramblings below...
> ---
> Note: build-tested only.
>
> Found with: $ rg "strncpy\("
> ---
> drivers/pnp/pnpbios/rsparser.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c
> index 2f31b212b1a5..70af7821d3fa 100644
> --- a/drivers/pnp/pnpbios/rsparser.c
> +++ b/drivers/pnp/pnpbios/rsparser.c
> @@ -454,8 +454,8 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
> switch (tag) {
So we've got a fixed-sized C string as a destination:
struct pnp_dev {
...
char name[PNP_NAME_LEN]; /* contains a human-readable name */
include/linux/pnp.h:#define PNP_NAME_LEN 50
And a funky "source length" calculation, which appears to be effectively
a u16 (it's either the low 3 bits of a u8, or a full u16);
int len ...
/* determine the type of tag */
if (p[0] & LARGE_TAG) { /* large tag */
len = (p[2] << 8) | p[1];
tag = p[0];
} else { /* small tag */
len = p[0] & 0x07;
tag = ((p[0] >> 3) & 0x0f);
}
The old code was doing:
case LARGE_TAG_ANSISTR:
strncpy(dev->name, p + 3,
len >= PNP_NAME_LEN ? PNP_NAME_LEN - 2 : len);
dev->name[len >=
PNP_NAME_LEN ? PNP_NAME_LEN - 1 : len] = '\0';
break;
The two conditionals are not the same -- the first is -2, the latter is
-1, but only when len >= PNP_NAME_LEN. This smells like a bug? For the
len >= PNP_NAME_LEN case, it will copy 48 bytes and then write a %NUL to
index 49 (byte 50). ... ... source byte 49 is ignored for no reason I
can see.
Regardless, the point is to copy no more than min(len, PNP_NAME_LEN - 1)
from "p + 3" to not overflow dev->name, and leaving it %NUL terminated.
So, I think what you have is identical behavior, and likely still
contains the 1 byte short bug, which I think is fine to keep as-is since
it's been like this forever and it's PNP...
-Kees
--
Kees Cook
next prev parent reply other threads:[~2023-10-20 0:04 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-19 23:28 [PATCH] PNP: replace deprecated strncpy with memcpy Justin Stitt
2023-10-20 0:04 ` Kees Cook [this message]
2023-10-20 17:51 ` Rafael J. Wysocki
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=202310191637.9EBDCC364@keescook \
--to=keescook@chromium.org \
--cc=justinstitt@google.com \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rafael.j.wysocki@intel.com \
/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.