diff mbox series

net: dsa: remove deprecated strncpy

Message ID 20230718-net-dsa-strncpy-v1-1-e84664747713@google.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net: dsa: remove deprecated strncpy | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1342 this patch: 1342
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/build_clang success Errors and warnings before: 1365 this patch: 1365
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 1365 this patch: 1365
netdev/checkpatch warning WARNING: From:/Signed-off-by: email name mismatch: 'From: "justinstitt@google.com" <justinstitt@google.com>' != 'Signed-off-by: Justin Stitt <justinstitt@google.com>'
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Justin Stitt July 18, 2023, 12:04 a.m. UTC
`strncpy` is deprecated for use on NUL-terminated destination strings [1].

Even call sites utilizing length-bounded destination buffers should
switch over to using `strtomem` or `strtomem_pad`. In this case,
however, the compiler is unable to determine the size of the `data`
buffer which renders `strtomem` unusable. Due to this, `strscpy`
should be used.

It should be noted that most call sites already zero-initialize the
destination buffer. However, I've opted to use `strscpy_pad` to maintain
the same exact behavior that `strncpy` produced (zero-padded tail up to
`len`).

Also see [3].

[1]: www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings
[2]: elixir.bootlin.com/linux/v6.3/source/net/ethtool/ioctl.c#L1944
[3]: manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html

Link: https://github.com/KSPP/linux/issues/90
Signed-off-by: Justin Stitt <justinstitt@google.com>
---
 net/dsa/slave.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)


---
base-commit: fdf0eaf11452d72945af31804e2a1048ee1b574c
change-id: 20230717-net-dsa-strncpy-844ca1111eb2

Best regards,

Comments

Kees Cook July 18, 2023, 6:05 p.m. UTC | #1
On July 17, 2023 5:04:19 PM PDT, justinstitt@google.com wrote:
>`strncpy` is deprecated for use on NUL-terminated destination strings [1].
>
>Even call sites utilizing length-bounded destination buffers should
>switch over to using `strtomem` or `strtomem_pad`. In this case,
>however, the compiler is unable to determine the size of the `data`
>buffer which renders `strtomem` unusable. Due to this, `strscpy`
>should be used.
>
>It should be noted that most call sites already zero-initialize the
>destination buffer. However, I've opted to use `strscpy_pad` to maintain
>the same exact behavior that `strncpy` produced (zero-padded tail up to
>`len`).
>
>Also see [3].
>
>[1]: www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings
>[2]: elixir.bootlin.com/linux/v6.3/source/net/ethtool/ioctl.c#L1944
>[3]: manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html
>
>Link: https://github.com/KSPP/linux/issues/90
>Signed-off-by: Justin Stitt <justinstitt@google.com>

This looks fine to me. I think the _pad variant is overkill (this region is already zero-initialized[1]), but it's a reasonable precaution for robustness.

Honestly I find the entire get_strings API to be very fragile given the lack of passing the length of the buffer, instead depending on the string set length lookups in each callback, but refactoring that looks like a ton of work for an uncertain benefit.

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

[1] https://elixir.bootlin.com/linux/v6.3/source/net/ethtool/ioctl.c#L1944
Nick Desaulniers July 18, 2023, 6:08 p.m. UTC | #2
On Mon, Jul 17, 2023 at 5:04 PM <justinstitt@google.com> wrote:
>
> `strncpy` is deprecated for use on NUL-terminated destination strings [1].
>
> Even call sites utilizing length-bounded destination buffers should
> switch over to using `strtomem` or `strtomem_pad`. In this case,
> however, the compiler is unable to determine the size of the `data`
> buffer which renders `strtomem` unusable. Due to this, `strscpy`
> should be used.
>
> It should be noted that most call sites already zero-initialize the
> destination buffer. However, I've opted to use `strscpy_pad` to maintain
> the same exact behavior that `strncpy` produced (zero-padded tail up to
> `len`).
>
> Also see [3].
>
> [1]: www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings
> [2]: elixir.bootlin.com/linux/v6.3/source/net/ethtool/ioctl.c#L1944
> [3]: manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html
>
> Link: https://github.com/KSPP/linux/issues/90
> Signed-off-by: Justin Stitt <justinstitt@google.com>
> ---
>  net/dsa/slave.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
> index 527b1d576460..c9f77b7e5895 100644
> --- a/net/dsa/slave.c
> +++ b/net/dsa/slave.c
> @@ -1056,10 +1056,10 @@ static void dsa_slave_get_strings(struct net_device *dev,
>         if (stringset == ETH_SS_STATS) {
>                 int len = ETH_GSTRING_LEN;
>
> -               strncpy(data, "tx_packets", len);
> -               strncpy(data + len, "tx_bytes", len);
> -               strncpy(data + 2 * len, "rx_packets", len);
> -               strncpy(data + 3 * len, "rx_bytes", len);
> +               strscpy_pad(data, "tx_packets", len);
> +               strscpy_pad(data + len, "tx_bytes", len);
> +               strscpy_pad(data + 2 * len, "rx_packets", len);
> +               strscpy_pad(data + 3 * len, "rx_bytes", len);

Thanks for the patch!

Consider adding a #include <linux/string.h> so that we stop having
such an indirect dependency in this TU.

Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>

>                 if (ds->ops->get_strings)
>                         ds->ops->get_strings(ds, dp->index, stringset,
>                                              data + 4 * len);
>
> ---
> base-commit: fdf0eaf11452d72945af31804e2a1048ee1b574c
> change-id: 20230717-net-dsa-strncpy-844ca1111eb2
>
> Best regards,
> --
> Justin Stitt <justinstitt@google.com>
>
Jakub Kicinski July 18, 2023, 7:11 p.m. UTC | #3
On Tue, 18 Jul 2023 11:05:23 -0700 Kees Cook wrote:
> Honestly I find the entire get_strings API to be very fragile given
> the lack of passing the length of the buffer, instead depending on
> the string set length lookups in each callback, but refactoring that
> looks like a ton of work for an uncertain benefit.

We have been adding better APIs for long term, and a print helper short
term - ethtool_sprintf(). Should we use ethtool_sprintf() here?
Andrew Lunn July 18, 2023, 7:31 p.m. UTC | #4
On Tue, Jul 18, 2023 at 12:11:16PM -0700, Jakub Kicinski wrote:
> On Tue, 18 Jul 2023 11:05:23 -0700 Kees Cook wrote:
> > Honestly I find the entire get_strings API to be very fragile given
> > the lack of passing the length of the buffer, instead depending on
> > the string set length lookups in each callback, but refactoring that
> > looks like a ton of work for an uncertain benefit.
> 
> We have been adding better APIs for long term, and a print helper short
> term - ethtool_sprintf(). Should we use ethtool_sprintf() here?

I was wondering about that as well. There is no variable expansion in
most cases, so the vsnprintf() is a waste of time.

Maybe we should actually add another helper:

ethtool_name_cpy(u8 **data, unsigned int index, const char *name);

Then over the next decade, slowly convert all drivers to it. And then
eventually replace the u8 with a struct including the length.

The netlink API is a bit better. It is one kAPI call which does
everything, and it holds RTNL. So it is less likely the number of
statistics will change between the calls into the driver.

	Andrew
Jakub Kicinski July 18, 2023, 7:41 p.m. UTC | #5
On Tue, 18 Jul 2023 21:31:04 +0200 Andrew Lunn wrote:
> On Tue, Jul 18, 2023 at 12:11:16PM -0700, Jakub Kicinski wrote:
> > On Tue, 18 Jul 2023 11:05:23 -0700 Kees Cook wrote:  
> > > Honestly I find the entire get_strings API to be very fragile given
> > > the lack of passing the length of the buffer, instead depending on
> > > the string set length lookups in each callback, but refactoring that
> > > looks like a ton of work for an uncertain benefit.  
> > 
> > We have been adding better APIs for long term, and a print helper short
> > term - ethtool_sprintf(). Should we use ethtool_sprintf() here?  
> 
> I was wondering about that as well. There is no variable expansion in
> most cases, so the vsnprintf() is a waste of time.
> 
> Maybe we should actually add another helper:
> 
> ethtool_name_cpy(u8 **data, unsigned int index, const char *name);

I wasn't sure if vsnprintf() is costly enough to bother, but SG.

Probably without the "unsigned int index", since the ethtool_sprintf()
API updates the first argument for the caller.

> Then over the next decade, slowly convert all drivers to it. And then
> eventually replace the u8 with a struct including the length.
> 
> The netlink API is a bit better. It is one kAPI call which does
> everything, and it holds RTNL. So it is less likely the number of
> statistics will change between the calls into the driver.
David Laight July 19, 2023, 8:47 a.m. UTC | #6
From: Andrew Lunn
> Sent: 18 July 2023 20:31
...
> Maybe we should actually add another helper:
> 
> ethtool_name_cpy(u8 **data, unsigned int index, const char *name);
> 
> Then over the next decade, slowly convert all drivers to it. And then
> eventually replace the u8 with a struct including the length.

Define the structure with the length from the start.
Add a wrapper that allows the length to be absent.
(Either ignoring the length or using 0/infinity to mean no length.)

Then you don't need to visit everywhere twice - just some places.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
diff mbox series

Patch

diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 527b1d576460..c9f77b7e5895 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1056,10 +1056,10 @@  static void dsa_slave_get_strings(struct net_device *dev,
 	if (stringset == ETH_SS_STATS) {
 		int len = ETH_GSTRING_LEN;
 
-		strncpy(data, "tx_packets", len);
-		strncpy(data + len, "tx_bytes", len);
-		strncpy(data + 2 * len, "rx_packets", len);
-		strncpy(data + 3 * len, "rx_bytes", len);
+		strscpy_pad(data, "tx_packets", len);
+		strscpy_pad(data + len, "tx_bytes", len);
+		strscpy_pad(data + 2 * len, "rx_packets", len);
+		strscpy_pad(data + 3 * len, "rx_bytes", len);
 		if (ds->ops->get_strings)
 			ds->ops->get_strings(ds, dp->index, stringset,
 					     data + 4 * len);