Introduce a userspace strscpy() implementation that matches the Linux kernel's strscpy() semantics. The function is built on top of glibc's strlcpy() and provides guaranteed NUL-termination along with proper truncation detection through its return value. The previous strncpy() calls had potential issues: strncpy() does not guarantee NUL-termination when the source string length equals or exceeds the destination buffer size. This required defensive patterns like pre-zeroing buffers or manually setting the last byte to NUL. The new strscpy() function always NUL-terminates the destination buffer unless the size is zero, and returns -E2BIG on truncation, making error handling cleaner and more consistent with kernel code. Note that unlike the kernel's strscpy(), this implementation uses strlcpy() internally, which reads the entire source string to determine its length. The kernel avoids this to prevent potential DoS attacks from extremely long untrusted strings. This is harmless for a userspace CLI tool like rtla where input sources are bounded and trusted. Replace all strncpy() calls in rtla with strscpy(), using sizeof() for buffer sizes instead of magic constants to ensure the sizes stay in sync with the actual buffer declarations. Also remove a now-redundant memset() call that was previously needed to work around strncpy() behavior. Signed-off-by: Wander Lairson Costa --- tools/tracing/rtla/src/timerlat_aa.c | 6 ++--- tools/tracing/rtla/src/utils.c | 34 ++++++++++++++++++++++++++-- tools/tracing/rtla/src/utils.h | 1 + 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 31e66ea2b144c..30ef56d644f9c 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -455,9 +455,9 @@ static int timerlat_aa_thread_handler(struct trace_seq *s, struct tep_record *re taa_data->thread_blocking_duration = duration; if (comm) - strncpy(taa_data->run_thread_comm, comm, MAX_COMM); + strscpy(taa_data->run_thread_comm, comm, sizeof(taa_data->run_thread_comm)); else - sprintf(taa_data->run_thread_comm, "<...>"); + strscpy(taa_data->run_thread_comm, "<...>", sizeof(taa_data->run_thread_comm)); } else { taa_data->thread_thread_sum += duration; @@ -519,7 +519,7 @@ static int timerlat_aa_sched_switch_handler(struct trace_seq *s, struct tep_reco tep_get_field_val(s, event, "next_pid", record, &taa_data->current_pid, 1); comm = tep_get_field_raw(s, event, "next_comm", record, &val, 1); - strncpy(taa_data->current_comm, comm, MAX_COMM); + strscpy(taa_data->current_comm, comm, sizeof(taa_data->current_comm)); /* * If this was a kworker, clean the last kworkers that ran. diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index b5a6007b108d2..e98288e55db15 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -722,8 +722,7 @@ static const int find_mount(const char *fs, char *mp, int sizeof_mp) if (!found) return 0; - memset(mp, 0, sizeof_mp); - strncpy(mp, mount_point, sizeof_mp - 1); + strscpy(mp, mount_point, sizeof_mp); debug_msg("Fs %s found at %s\n", fs, mp); return 1; @@ -1036,6 +1035,37 @@ int strtoi(const char *s, int *res) return 0; } +/** + * strscpy - Copy a C-string into a sized buffer + * @dst: Where to copy the string to + * @src: Where to copy the string from + * @count: Size of destination buffer + * + * Copy the source string @src, or as much of it as fits, into the destination + * @dst buffer. The destination @dst buffer is always NUL-terminated, unless + * it's zero-sized. + * + * This is a userspace implementation matching the kernel's strscpy() semantics, + * built on top of glibc's strlcpy(). + * + * Returns the number of characters copied (not including the trailing NUL) + * or -E2BIG if @count is 0 or the copy was truncated. + */ +ssize_t strscpy(char *dst, const char *src, size_t count) +{ + size_t len; + + if (count == 0) + return -E2BIG; + + len = strlcpy(dst, src, count); + + if (len >= count) + return -E2BIG; + + return (ssize_t) len; +} + static inline void fatal_alloc(void) { fatal("Error allocating memory\n"); diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 8323c999260c2..25b08fc5e199a 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -97,6 +97,7 @@ static inline int have_libcpupower_support(void) { return 0; } #endif /* HAVE_LIBCPUPOWER_SUPPORT */ int auto_house_keeping(cpu_set_t *monitored_cpus); __attribute__((__warn_unused_result__)) int strtoi(const char *s, int *res); +ssize_t strscpy(char *dst, const char *src, size_t count); #define ns_to_usf(x) (((double)x/1000)) #define ns_to_per(total, part) ((part * 100) / (double)total) -- 2.52.0