Re: [PATCH] wifi: ipw2x00: replace deprecated strncpy with strscpy

From: Justin Stitt
Date: Tue Aug 01 2023 - 20:55:16 EST


On Tue, Aug 1, 2023 at 4:25 PM Kees Cook <keescook@xxxxxxxxxxxx> wrote:
>
> On Tue, Aug 01, 2023 at 09:53:36PM +0000, Justin Stitt wrote:
> > `strncpy` is deprecated for use on NUL-terminated destination strings [1].
> >
> > We can massively simplify the implementation by removing the ternary
> > check for the smaller of `count` and `sizeof(buffer) - 1` as `strscpy`
> > guarantees NUL-termination of its destination buffer [2]. This also
> > means we do not need to explicity set the one past-the-last index to
> > zero as `strscpy` handles this.
> >
> > Furthermore, we can also utilize `strscpy`'s return value to populate
> > `len` and simply pass in `sizeof(buffer)` to the `strscpy` invocation
> > itself.
> >
> > [1]: www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings
> > [2]: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html
> >
> > Link: https://github.com/KSPP/linux/issues/90
> > Cc: linux-hardening@xxxxxxxxxxxxxxx
> > Signed-off-by: Justin Stitt <justinstitt@xxxxxxxxxx>
> > ---
> > drivers/net/wireless/intel/ipw2x00/ipw2200.c | 5 +----
> > 1 file changed, 1 insertion(+), 4 deletions(-)
> >
> > diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> > index dfe0f74369e6..8f2a834dbe04 100644
> > --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> > +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> > @@ -1462,15 +1462,12 @@ static ssize_t scan_age_store(struct device *d, struct device_attribute *attr,
> > struct ipw_priv *priv = dev_get_drvdata(d);
> > struct net_device *dev = priv->net_dev;
> > char buffer[] = "00000000";
> > - unsigned long len =
> > - (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
> > unsigned long val;
> > char *p = buffer;
> >
> > IPW_DEBUG_INFO("enter\n");
> >
> > - strncpy(buffer, buf, len);
> > - buffer[len] = 0;
> > + ssize_t len = strscpy(buffer, buf, sizeof(buffer));
>
> This means "len" could become -E2BIG, which changes the behavior of this
> function. The earlier manipulation of "len" seems to be trying to
> explicitly allow for truncation, though. (if buffer could hold more than
> "count", copy "count", otherwise copy less)
>
> So it looks like -E2BIG should be ignored here? But since this is a
> sysfs node (static DEVICE_ATTR_RW(scan_age)), I actually think the
> original code may be bugged: it should return how much was read from
> the input... and technically this was true, but it seems the intent
> is to consume the entire buffer and set a result. It's possible "len"
> is entirely unneeded and this should just return "count"?
>
> And, honestly, I think it's likely that most of this entire routine should
> be thrown out in favor of just using kstrtoul() with base 0, as sysfs
> input buffers are always NUL-terminated. (See kernfs_fop_write_iter().)

Great suggestion, instead of v2'ing this patch I've opted to create a
new one due to it being slightly larger scope than just replacing
`strncpy`.

Patch: https://lore.kernel.org/r/20230802-wifi-ipw2x00-refactor-v1-1-6047659410d4@xxxxxxxxxx

>
>
> diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> index dfe0f74369e6..780f5613e279 100644
> --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
> @@ -1461,25 +1461,11 @@ static ssize_t scan_age_store(struct device *d, struct device_attribute *attr,
> {
> struct ipw_priv *priv = dev_get_drvdata(d);
> struct net_device *dev = priv->net_dev;
> - char buffer[] = "00000000";
> - unsigned long len =
> - (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
> unsigned long val;
> - char *p = buffer;
>
> IPW_DEBUG_INFO("enter\n");
>
> - strncpy(buffer, buf, len);
> - buffer[len] = 0;
> -
> - if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
> - p++;
> - if (p[0] == 'x' || p[0] == 'X')
> - p++;
> - val = simple_strtoul(p, &p, 16);
> - } else
> - val = simple_strtoul(p, &p, 10);
> - if (p == buffer) {
> + if (kstrtoul(buf, 0, &val)) {
> IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
> } else {
> priv->ieee->scan_age = val;
> @@ -1487,7 +1473,7 @@ static ssize_t scan_age_store(struct device *d, struct device_attribute *attr,
> }
>
> IPW_DEBUG_INFO("exit\n");
> - return len;
> + return count;
> }
>
> static DEVICE_ATTR_RW(scan_age);
>
>
> -Kees
>
> >
> > if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
> > p++;
> >
> > ---
> > base-commit: 5d0c230f1de8c7515b6567d9afba1f196fb4e2f4
> > change-id: 20230801-drivers-net-wireless-intel-ipw2x00-d7ee2dd17032
> >
> > Best regards,
> > --
> > Justin Stitt <justinstitt@xxxxxxxxxx>
> >
>
> --
> Kees Cook