diff mbox series

trace: events: cleanup deprecated strncpy uses

Message ID 20240401-strncpy-include-trace-events-mdio-h-v1-1-9cb5a4cda116@google.com (mailing list archive)
State Not Applicable
Headers show
Series trace: events: cleanup deprecated strncpy uses | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Justin Stitt April 1, 2024, 11:48 p.m. UTC
strncpy() is deprecated for use on NUL-terminated destination strings
[1] and as such we should prefer more robust and less ambiguous string
interfaces.

For 2 out of 3 of these changes we can simply swap in strscpy() as it
guarantess NUL-termination which is needed for the following trace
print.

trace_rpcgss_context() should use memcpy as its format specifier %.*s
allows for the length to be specifier (__entry->len). Due to this,
acceptor does not technically need to be NUL-terminated. Moreover,
swapping in strscpy() and keeping everything else the same could result
in truncation of the source string by one byte. To remedy this, we could
use `len + 1` but I am unsure of the size of the destination buffer so a
simple memcpy should suffice.
|	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",
|		__entry->window_size, __entry->expiry, __entry->now,
|		__entry->timeout, __entry->len, __get_str(acceptor))

I suspect acceptor not to naturally be a NUL-terminated string due to
the presence of some stringify methods.
|	.crstringify_acceptor	= gss_stringify_acceptor,

Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1]
Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2]
Link: https://github.com/KSPP/linux/issues/90
Cc: linux-hardening@vger.kernel.org
Signed-off-by: Justin Stitt <justinstitt@google.com>
---
Note: build-tested only.

Found with: $ rg "strncpy\("
---
 include/trace/events/mdio.h   | 2 +-
 include/trace/events/rpcgss.h | 2 +-
 include/trace/events/sock.h   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)


---
base-commit: 928a87efa42302a23bb9554be081a28058495f22
change-id: 20240401-strncpy-include-trace-events-mdio-h-0a325676b468

Best regards,
--
Justin Stitt <justinstitt@google.com>

Comments

Chuck Lever III April 3, 2024, 1:30 p.m. UTC | #1
On Mon, Apr 01, 2024 at 11:48:52PM +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 string
> interfaces.
> 
> For 2 out of 3 of these changes we can simply swap in strscpy() as it
> guarantess NUL-termination which is needed for the following trace
> print.
> 
> trace_rpcgss_context() should use memcpy as its format specifier %.*s
> allows for the length to be specifier (__entry->len). Due to this,
> acceptor does not technically need to be NUL-terminated. Moreover,
> swapping in strscpy() and keeping everything else the same could result
> in truncation of the source string by one byte. To remedy this, we could
> use `len + 1` but I am unsure of the size of the destination buffer so a
> simple memcpy should suffice.
> |	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",
> |		__entry->window_size, __entry->expiry, __entry->now,
> |		__entry->timeout, __entry->len, __get_str(acceptor))
> 
> I suspect acceptor not to naturally be a NUL-terminated string due to
> the presence of some stringify methods.
> |	.crstringify_acceptor	= gss_stringify_acceptor,
> 
> Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1]
> Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2]
> Link: https://github.com/KSPP/linux/issues/90
> Cc: linux-hardening@vger.kernel.org
> Signed-off-by: Justin Stitt <justinstitt@google.com>
> ---
> Note: build-tested only.
> 
> Found with: $ rg "strncpy\("
> ---
>  include/trace/events/mdio.h   | 2 +-
>  include/trace/events/rpcgss.h | 2 +-
>  include/trace/events/sock.h   | 2 +-
>  3 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/include/trace/events/mdio.h b/include/trace/events/mdio.h
> index 0f241cbe00ab..285b3e4f83ba 100644
> --- a/include/trace/events/mdio.h
> +++ b/include/trace/events/mdio.h
> @@ -25,7 +25,7 @@ TRACE_EVENT_CONDITION(mdio_access,
>  	),
>  
>  	TP_fast_assign(
> -		strncpy(__entry->busid, bus->id, MII_BUS_ID_SIZE);
> +		strscpy(__entry->busid, bus->id, MII_BUS_ID_SIZE);
>  		__entry->read = read;
>  		__entry->addr = addr;
>  		__entry->regnum = regnum;
> diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h
> index ba2d96a1bc2f..274c297f1b15 100644
> --- a/include/trace/events/rpcgss.h
> +++ b/include/trace/events/rpcgss.h
> @@ -618,7 +618,7 @@ TRACE_EVENT(rpcgss_context,
>  		__entry->timeout = timeout;
>  		__entry->window_size = window_size;
>  		__entry->len = len;
> -		strncpy(__get_str(acceptor), data, len);
> +		memcpy(__get_str(acceptor), data, len);
>  	),
>  
>  	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",
> diff --git a/include/trace/events/sock.h b/include/trace/events/sock.h
> index fd206a6ab5b8..1d0b98e6b2cc 100644
> --- a/include/trace/events/sock.h
> +++ b/include/trace/events/sock.h
> @@ -109,7 +109,7 @@ TRACE_EVENT(sock_exceed_buf_limit,
>  	),
>  
>  	TP_fast_assign(
> -		strncpy(__entry->name, prot->name, 32);
> +		strscpy(__entry->name, prot->name, 32);
>  		__entry->sysctl_mem[0] = READ_ONCE(prot->sysctl_mem[0]);
>  		__entry->sysctl_mem[1] = READ_ONCE(prot->sysctl_mem[1]);
>  		__entry->sysctl_mem[2] = READ_ONCE(prot->sysctl_mem[2]);
> 
> ---
> base-commit: 928a87efa42302a23bb9554be081a28058495f22
> change-id: 20240401-strncpy-include-trace-events-mdio-h-0a325676b468
> 
> Best regards,
> --
> Justin Stitt <justinstitt@google.com>
> 

Acked-by: Chuck Lever <chuck.lever@oracle.com>
Steven Rostedt April 10, 2024, 3:36 p.m. UTC | #2
On Mon, 01 Apr 2024 23:48:52 +0000
Justin Stitt <justinstitt@google.com> wrote:

> diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h
> index ba2d96a1bc2f..274c297f1b15 100644
> --- a/include/trace/events/rpcgss.h
> +++ b/include/trace/events/rpcgss.h
> @@ -618,7 +618,7 @@ TRACE_EVENT(rpcgss_context,
>  		__entry->timeout = timeout;
>  		__entry->window_size = window_size;
>  		__entry->len = len;
> -		strncpy(__get_str(acceptor), data, len);
> +		memcpy(__get_str(acceptor), data, len);
>  	),
>  
>  	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",

WTF, that code is just buggy. Looking at the rpcgss_context event we have:

> TRACE_EVENT(rpcgss_context,
>         TP_PROTO(
>                 u32 window_size,
>                 unsigned long expiry,
>                 unsigned long now,
>                 unsigned int timeout,
>                 unsigned int len,
>                 const u8 *data
>         ),
> 
>         TP_ARGS(window_size, expiry, now, timeout, len, data),
> 
>         TP_STRUCT__entry(
>                 __field(unsigned long, expiry)
>                 __field(unsigned long, now)
>                 __field(unsigned int, timeout)
>                 __field(u32, window_size)
>                 __field(int, len)
>                 __string(acceptor, data)

The __string() macro expects "data" to be a string and does *not* check
length when copying.

If anything, it needs to be:

		__string_len(acceptor, data, len)

as the macro code has changed recently, and the current code will crash!

>         ),
> 
>         TP_fast_assign(
>                 __entry->expiry = expiry;
>                 __entry->now = now;
>                 __entry->timeout = timeout;
>                 __entry->window_size = window_size;
>                 __entry->len = len;
>                 strncpy(__get_str(acceptor), data, len);

Then this needs to be:

		__assign_str(acceptor, data);

Note, the length is now saved via __string_len() and not needed here.

I'll go send a patch to fix this.

-- Steve


>         ),
Chuck Lever III April 10, 2024, 3:41 p.m. UTC | #3
On Wed, Apr 10, 2024 at 11:36:14AM -0400, Steven Rostedt wrote:
> On Mon, 01 Apr 2024 23:48:52 +0000
> Justin Stitt <justinstitt@google.com> wrote:
> 
> > diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h
> > index ba2d96a1bc2f..274c297f1b15 100644
> > --- a/include/trace/events/rpcgss.h
> > +++ b/include/trace/events/rpcgss.h
> > @@ -618,7 +618,7 @@ TRACE_EVENT(rpcgss_context,
> >  		__entry->timeout = timeout;
> >  		__entry->window_size = window_size;
> >  		__entry->len = len;
> > -		strncpy(__get_str(acceptor), data, len);
> > +		memcpy(__get_str(acceptor), data, len);
> >  	),
> >  
> >  	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",
> 
> WTF, that code is just buggy. Looking at the rpcgss_context event we have:
> 
> > TRACE_EVENT(rpcgss_context,
> >         TP_PROTO(
> >                 u32 window_size,
> >                 unsigned long expiry,
> >                 unsigned long now,
> >                 unsigned int timeout,
> >                 unsigned int len,
> >                 const u8 *data
> >         ),
> > 
> >         TP_ARGS(window_size, expiry, now, timeout, len, data),
> > 
> >         TP_STRUCT__entry(
> >                 __field(unsigned long, expiry)
> >                 __field(unsigned long, now)
> >                 __field(unsigned int, timeout)
> >                 __field(u32, window_size)
> >                 __field(int, len)
> >                 __string(acceptor, data)
> 
> The __string() macro expects "data" to be a string and does *not* check
> length when copying.
> 
> If anything, it needs to be:
> 
> 		__string_len(acceptor, data, len)
> 
> as the macro code has changed recently, and the current code will crash!

A general question:

Is there a test suite we should run regularly to build some
confidence in the kernel's observability apparatus? We're building
a menagerie of tests around kdevops, and one area where we know
there is a testing gap is the tracepoints in NFSD and SunRPC.


> >         ),
> > 
> >         TP_fast_assign(
> >                 __entry->expiry = expiry;
> >                 __entry->now = now;
> >                 __entry->timeout = timeout;
> >                 __entry->window_size = window_size;
> >                 __entry->len = len;
> >                 strncpy(__get_str(acceptor), data, len);
> 
> Then this needs to be:
> 
> 		__assign_str(acceptor, data);
> 
> Note, the length is now saved via __string_len() and not needed here.
> 
> I'll go send a patch to fix this.
> 
> -- Steve
> 
> 
> >         ),
Steven Rostedt April 10, 2024, 3:56 p.m. UTC | #4
On Wed, 10 Apr 2024 11:41:18 -0400
Chuck Lever <chuck.lever@oracle.com> wrote:

> > If anything, it needs to be:
> > 
> > 		__string_len(acceptor, data, len)
> > 
> > as the macro code has changed recently, and the current code will crash!  
> 
> A general question:
> 
> Is there a test suite we should run regularly to build some
> confidence in the kernel's observability apparatus? We're building
> a menagerie of tests around kdevops, and one area where we know
> there is a testing gap is the tracepoints in NFSD and SunRPC.

I try to add self tests within the macros. As __assign_str() is going to
turn into just __assign_str(field) (instead of __assign_str(field, src)), I
added a test to make sure what was passed to __string() is also what
__assign_str() gets. But using strcpy() in place of __assign_str() really
is doing something that one should not be doing.

There's checks in the tracepoint code as well for things like referencing a
pointer that was not saved in the event, and other things. But a generic
test on a custom trace event, I don't really have.

Note, you could enable CONFIG_TRACE_EVENT_INJECT where an "inject" file is
created, and you can write into it:

 # cd /sys/kernel/tracing
 # echo 1 > events/rpcgss/rpcgss_context/enable
 # echo 'expiry=123456 now=222' > events/rpcgss/rpcgss_context/inject
 # cat trace
bash-843     [001] .....   720.083189: rpcgss_context: win_size=0 expiry=123456 now=222 timeout=0 acceptor=

And you could use that for testing.

-- Steve
diff mbox series

Patch

diff --git a/include/trace/events/mdio.h b/include/trace/events/mdio.h
index 0f241cbe00ab..285b3e4f83ba 100644
--- a/include/trace/events/mdio.h
+++ b/include/trace/events/mdio.h
@@ -25,7 +25,7 @@  TRACE_EVENT_CONDITION(mdio_access,
 	),
 
 	TP_fast_assign(
-		strncpy(__entry->busid, bus->id, MII_BUS_ID_SIZE);
+		strscpy(__entry->busid, bus->id, MII_BUS_ID_SIZE);
 		__entry->read = read;
 		__entry->addr = addr;
 		__entry->regnum = regnum;
diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h
index ba2d96a1bc2f..274c297f1b15 100644
--- a/include/trace/events/rpcgss.h
+++ b/include/trace/events/rpcgss.h
@@ -618,7 +618,7 @@  TRACE_EVENT(rpcgss_context,
 		__entry->timeout = timeout;
 		__entry->window_size = window_size;
 		__entry->len = len;
-		strncpy(__get_str(acceptor), data, len);
+		memcpy(__get_str(acceptor), data, len);
 	),
 
 	TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s",
diff --git a/include/trace/events/sock.h b/include/trace/events/sock.h
index fd206a6ab5b8..1d0b98e6b2cc 100644
--- a/include/trace/events/sock.h
+++ b/include/trace/events/sock.h
@@ -109,7 +109,7 @@  TRACE_EVENT(sock_exceed_buf_limit,
 	),
 
 	TP_fast_assign(
-		strncpy(__entry->name, prot->name, 32);
+		strscpy(__entry->name, prot->name, 32);
 		__entry->sysctl_mem[0] = READ_ONCE(prot->sysctl_mem[0]);
 		__entry->sysctl_mem[1] = READ_ONCE(prot->sysctl_mem[1]);
 		__entry->sysctl_mem[2] = READ_ONCE(prot->sysctl_mem[2]);