* [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy()
@ 2026-02-20 18:20 Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 1/7] selftests/bpf: Add simple strscpy() implementation Ihor Solodrai
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
This series introduces a simplified implementation of strscpy() to
selftests/bpf and then replaces strcpy/strncpy usages across the tests
with it.
For context see a discussion that prompted this series [1].
[1] https://lore.kernel.org/bpf/CAADnVQ+9uw2_o388j43EWiAPdMB=3FLx2jq-9zRSvqrv-wgRag@mail.gmail.com/
Suggested-by: Alexei Starovoitov <ast@kernel.org>
Ihor Solodrai (7):
selftests/bpf: Add simple strscpy() implementation
selftests/bpf: Add strscpy_cat()
selftests/bpf: Replace strcpy() calls with strscpy()
selftests/bpf: Replace strncpy() with strscpy()
selftests/bpf: Use strscpy_cat() in the test_loader
selftests/bpf: Use strscpy in bpftool_helpers.c
selftests/bpf: Use memcpy() for bounded non-NULL-terminated copies
tools/testing/selftests/bpf/bpf_util.h | 66 +++++++++++++++----
tools/testing/selftests/bpf/bpftool_helpers.c | 16 +++--
tools/testing/selftests/bpf/cgroup_helpers.c | 2 +-
tools/testing/selftests/bpf/network_helpers.c | 5 +-
.../testing/selftests/bpf/prog_tests/align.c | 2 +-
.../selftests/bpf/prog_tests/bpf_iter.c | 3 +-
.../selftests/bpf/prog_tests/bpf_tcp_ca.c | 2 +-
.../selftests/bpf/prog_tests/ctx_rewrite.c | 6 +-
.../selftests/bpf/prog_tests/flow_dissector.c | 4 +-
.../bpf/prog_tests/queue_stack_map.c | 4 +-
.../selftests/bpf/prog_tests/setget_sockopt.c | 2 +-
.../bpf/prog_tests/skc_to_unix_sock.c | 2 +-
.../selftests/bpf/prog_tests/sockopt_sk.c | 2 +-
.../bpf/prog_tests/task_local_data.h | 2 +-
.../selftests/bpf/prog_tests/tc_redirect.c | 2 +-
.../selftests/bpf/prog_tests/test_veristat.c | 4 +-
tools/testing/selftests/bpf/test_loader.c | 8 +--
tools/testing/selftests/bpf/test_progs.c | 2 +-
tools/testing/selftests/bpf/test_verifier.c | 2 +-
tools/testing/selftests/bpf/xdp_features.c | 3 +-
tools/testing/selftests/bpf/xdp_hw_metadata.c | 4 +-
21 files changed, 93 insertions(+), 50 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH bpf v1 1/7] selftests/bpf: Add simple strscpy() implementation
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat() Ihor Solodrai
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
Replace bpf_strlcpy() in bpf_util.h with a sized_strscpy(), which is a
simplified sized_strscpy() from the kernel (lib/string.c). It:
* takes a count (destination size) parameter
* guarantees NULL-termination
* returns the number of characters copied or -E2BIG
Re-define strscpy macro to sized_strscpy.
Fixup the single existing bpf_strlcpy() call in cgroup_helpers.c
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/bpf_util.h | 38 +++++++++++++-------
tools/testing/selftests/bpf/cgroup_helpers.c | 2 +-
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h
index 4bc2d25f33e1..8c95ef7ed7c0 100644
--- a/tools/testing/selftests/bpf/bpf_util.h
+++ b/tools/testing/selftests/bpf/bpf_util.h
@@ -21,25 +21,37 @@ static inline unsigned int bpf_num_possible_cpus(void)
return possible_cpus;
}
-/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst
- * is zero-terminated string no matter what (unless sz == 0, in which case
- * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs
- * in what is returned. Given this is internal helper, it's trivial to extend
- * this, when necessary. Use this instead of strncpy inside libbpf source code.
+/*
+ * Simplified strscpy() implementation. The kernel one is in lib/string.c
*/
-static inline void bpf_strlcpy(char *dst, const char *src, size_t sz)
+static inline ssize_t sized_strscpy(char *dest, const char *src, size_t count)
{
- size_t i;
+ long res = 0;
- if (sz == 0)
- return;
+ if (count == 0)
+ return -E2BIG;
- sz--;
- for (i = 0; i < sz && src[i]; i++)
- dst[i] = src[i];
- dst[i] = '\0';
+ while (count > 1) {
+ char c;
+
+ c = src[res];
+ dest[res] = c;
+ if (!c)
+ return res;
+ res++;
+ count--;
+ }
+
+ /* Force NUL-termination. */
+ dest[res] = '\0';
+
+ /* Return E2BIG if the source didn't stop */
+ return src[res] ? -E2BIG : res;
}
+#undef strscpy /* Redefine the placeholder from tools/include/linux/string.h */
+#define strscpy sized_strscpy
+
#define __bpf_percpu_val_align __attribute__((__aligned__(8)))
#define BPF_DECLARE_PERCPU(type, name) \
diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
index 20cede4db3ce..9661cceb62be 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.c
+++ b/tools/testing/selftests/bpf/cgroup_helpers.c
@@ -86,7 +86,7 @@ static int __enable_controllers(const char *cgroup_path, const char *controllers
enable[len] = 0;
close(fd);
} else {
- bpf_strlcpy(enable, controllers, sizeof(enable));
+ strscpy(enable, controllers, sizeof(enable));
}
snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat()
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 1/7] selftests/bpf: Add simple strscpy() implementation Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 23:04 ` Alexei Starovoitov
2026-02-20 18:20 ` [PATCH bpf v1 3/7] selftests/bpf: Replace strcpy() calls with strscpy() Ihor Solodrai
` (4 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
Add sized_strscpy_cat() to bpf_util.h, which concatenates multiple
strings into a destination buffer with strscpy().
Add strscpy_cat() macro with varargs.
This is a convenient helper that provides the same guarantees as
strscpy(), but for a case when multiple strings need to be
concatenated into destination.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/bpf_util.h | 28 ++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h
index 8c95ef7ed7c0..61bb22c3e517 100644
--- a/tools/testing/selftests/bpf/bpf_util.h
+++ b/tools/testing/selftests/bpf/bpf_util.h
@@ -52,6 +52,34 @@ static inline ssize_t sized_strscpy(char *dest, const char *src, size_t count)
#undef strscpy /* Redefine the placeholder from tools/include/linux/string.h */
#define strscpy sized_strscpy
+/*
+ * strscpy() analogue that concatenates multiple strings into a buffer
+ */
+static inline ssize_t sized_strscpy_cat(char *dest, size_t dest_sz,
+ const char * const *srcs, size_t n)
+{
+ ssize_t pos = 0;
+
+ if (dest_sz == 0)
+ return -E2BIG;
+
+ for (size_t i = 0; i < n; i++) {
+ ssize_t res = strscpy(dest + pos, srcs[i], dest_sz - pos);
+
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+
+ return pos;
+}
+
+#define strscpy_cat(dest, count, ...) \
+ sized_strscpy_cat(dest, count, \
+ (const char * const[]){ __VA_ARGS__ }, \
+ ARRAY_SIZE(((const char * const[]){ __VA_ARGS__ })))
+
+
#define __bpf_percpu_val_align __attribute__((__aligned__(8)))
#define BPF_DECLARE_PERCPU(type, name) \
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 3/7] selftests/bpf: Replace strcpy() calls with strscpy()
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 1/7] selftests/bpf: Add simple strscpy() implementation Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat() Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 4/7] selftests/bpf: Replace strncpy() " Ihor Solodrai
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
strcpy() does not perform bounds checking and is considered deprecated
[1]. Replace strcpy() calls with strscpy(), passing size of the
destination buffer.
[1] https://docs.kernel.org/process/deprecated.html#strcpy
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/network_helpers.c | 2 +-
tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c | 2 +-
tools/testing/selftests/bpf/prog_tests/setget_sockopt.c | 2 +-
tools/testing/selftests/bpf/prog_tests/sockopt_sk.c | 2 +-
tools/testing/selftests/bpf/prog_tests/test_veristat.c | 4 ++--
tools/testing/selftests/bpf/xdp_features.c | 3 ++-
6 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/bpf/network_helpers.c b/tools/testing/selftests/bpf/network_helpers.c
index 0a6a5561bed3..5374b7e16d53 100644
--- a/tools/testing/selftests/bpf/network_helpers.c
+++ b/tools/testing/selftests/bpf/network_helpers.c
@@ -432,7 +432,7 @@ int make_sockaddr(int family, const char *addr_str, __u16 port,
memset(addr, 0, sizeof(*sun));
sun->sun_family = family;
sun->sun_path[0] = 0;
- strcpy(sun->sun_path + 1, addr_str);
+ strscpy(sun->sun_path + 1, addr_str, sizeof(sun->sun_path) - 1);
if (len)
*len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(addr_str);
return 0;
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
index b7d1b52309d0..2afedd08c037 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
@@ -281,7 +281,7 @@ static void test_dctcp_fallback(void)
dctcp_skel = bpf_dctcp__open();
if (!ASSERT_OK_PTR(dctcp_skel, "dctcp_skel"))
return;
- strcpy(dctcp_skel->rodata->fallback_cc, "cubic");
+ strscpy(dctcp_skel->rodata->fallback_cc, "cubic", sizeof(dctcp_skel->rodata->fallback_cc));
if (!ASSERT_OK(bpf_dctcp__load(dctcp_skel), "bpf_dctcp__load"))
goto done;
diff --git a/tools/testing/selftests/bpf/prog_tests/setget_sockopt.c b/tools/testing/selftests/bpf/prog_tests/setget_sockopt.c
index e4dac529d424..7905fbe11447 100644
--- a/tools/testing/selftests/bpf/prog_tests/setget_sockopt.c
+++ b/tools/testing/selftests/bpf/prog_tests/setget_sockopt.c
@@ -212,7 +212,7 @@ void test_setget_sockopt(void)
if (!ASSERT_OK_PTR(skel, "open skel"))
goto done;
- strcpy(skel->rodata->veth, "binddevtest1");
+ strscpy(skel->rodata->veth, "binddevtest1", sizeof(skel->rodata->veth));
skel->rodata->veth_ifindex = if_nametoindex("binddevtest1");
if (!ASSERT_GT(skel->rodata->veth_ifindex, 0, "if_nametoindex"))
goto done;
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
index ba6b3ec1156a..fb0414292af6 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c
@@ -142,7 +142,7 @@ static int getsetsockopt(void)
/* TCP_CONGESTION can extend the string */
- strcpy(buf.cc, "nv");
+ strscpy(buf.cc, "nv", sizeof(buf.cc));
err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv"));
if (err) {
log_err("Failed to call setsockopt(TCP_CONGESTION)");
diff --git a/tools/testing/selftests/bpf/prog_tests/test_veristat.c b/tools/testing/selftests/bpf/prog_tests/test_veristat.c
index b38c16b4247f..de3aab986060 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_veristat.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_veristat.c
@@ -24,9 +24,9 @@ static struct fixture *init_fixture(void)
/* for no_alu32 and cpuv4 veristat is in parent folder */
if (access("./veristat", F_OK) == 0)
- strcpy(fix->veristat, "./veristat");
+ strscpy(fix->veristat, "./veristat", sizeof(fix->veristat));
else if (access("../veristat", F_OK) == 0)
- strcpy(fix->veristat, "../veristat");
+ strscpy(fix->veristat, "../veristat", sizeof(fix->veristat));
else
PRINT_FAIL("Can't find veristat binary");
diff --git a/tools/testing/selftests/bpf/xdp_features.c b/tools/testing/selftests/bpf/xdp_features.c
index 595c79141cf3..5649d2dda9cc 100644
--- a/tools/testing/selftests/bpf/xdp_features.c
+++ b/tools/testing/selftests/bpf/xdp_features.c
@@ -16,6 +16,7 @@
#include <network_helpers.h>
+#include "bpf_util.h"
#include "xdp_features.skel.h"
#include "xdp_features.h"
@@ -212,7 +213,7 @@ static void set_env_default(void)
env.feature.drv_feature = NETDEV_XDP_ACT_NDO_XMIT;
env.feature.action = -EINVAL;
env.ifindex = -ENODEV;
- strcpy(env.ifname, "unknown");
+ strscpy(env.ifname, "unknown", sizeof(env.ifname));
make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_CTRL_PORT,
&env.dut_ctrl_addr, NULL);
make_sockaddr(AF_INET6, "::ffff:127.0.0.1", DUT_ECHO_PORT,
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 4/7] selftests/bpf: Replace strncpy() with strscpy()
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
` (2 preceding siblings ...)
2026-02-20 18:20 ` [PATCH bpf v1 3/7] selftests/bpf: Replace strcpy() calls with strscpy() Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 5/7] selftests/bpf: Use strscpy_cat() in the test_loader Ihor Solodrai
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
strncpy() does not guarantee NUL-termination and is considered
deprecated [1]. Replace strncpy() calls with strscpy().
[1] https://docs.kernel.org/process/deprecated.html#strncpy-on-nul-terminated-strings
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/network_helpers.c | 3 +--
tools/testing/selftests/bpf/prog_tests/align.c | 2 +-
tools/testing/selftests/bpf/prog_tests/bpf_iter.c | 3 +--
tools/testing/selftests/bpf/prog_tests/flow_dissector.c | 4 ++--
tools/testing/selftests/bpf/prog_tests/queue_stack_map.c | 4 ++--
tools/testing/selftests/bpf/prog_tests/skc_to_unix_sock.c | 2 +-
tools/testing/selftests/bpf/prog_tests/task_local_data.h | 2 +-
tools/testing/selftests/bpf/prog_tests/tc_redirect.c | 2 +-
tools/testing/selftests/bpf/test_progs.c | 2 +-
tools/testing/selftests/bpf/xdp_hw_metadata.c | 4 ++--
10 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/tools/testing/selftests/bpf/network_helpers.c b/tools/testing/selftests/bpf/network_helpers.c
index 5374b7e16d53..e169c6ece524 100644
--- a/tools/testing/selftests/bpf/network_helpers.c
+++ b/tools/testing/selftests/bpf/network_helpers.c
@@ -581,8 +581,7 @@ int open_tuntap(const char *dev_name, bool need_mac)
return -1;
ifr.ifr_flags = IFF_NO_PI | (need_mac ? IFF_TAP : IFF_TUN);
- strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1);
- ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ strscpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
err = ioctl(fd, TUNSETIFF, &ifr);
if (!ASSERT_OK(err, "ioctl(TUNSETIFF)")) {
diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c
index 24c509ce4e5b..7b6e530a586c 100644
--- a/tools/testing/selftests/bpf/prog_tests/align.c
+++ b/tools/testing/selftests/bpf/prog_tests/align.c
@@ -633,7 +633,7 @@ static int do_test_single(struct bpf_align_test *test)
} else {
ret = 0;
/* We make a local copy so that we can strtok() it */
- strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
+ strscpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
start = strstr(bpf_vlog_copy, main_pass_start);
if (!start) {
ret = 1;
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
index 5225d69bf79b..c69080ca14f5 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
@@ -346,8 +346,7 @@ static void test_task_sleepable(void)
close(finish_pipe[1]);
test_data = malloc(sizeof(char) * 10);
- strncpy(test_data, "test_data", 10);
- test_data[9] = '\0';
+ strscpy(test_data, "test_data", 10);
test_data_long = malloc(sizeof(char) * 5000);
for (int i = 0; i < 5000; ++i) {
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
index 08bae13248c4..22dbd13cf592 100644
--- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
+++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
@@ -570,7 +570,7 @@ static int create_tap(const char *ifname)
};
int fd, ret;
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ strscpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
fd = open("/dev/net/tun", O_RDWR);
if (fd < 0)
@@ -599,7 +599,7 @@ static int ifup(const char *ifname)
struct ifreq ifr = {};
int sk, ret;
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ strscpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
sk = socket(PF_INET, SOCK_DGRAM, 0);
if (sk < 0)
diff --git a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
index a043af9cd6d9..a3c6acc33d5d 100644
--- a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
+++ b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
@@ -28,9 +28,9 @@ static void test_queue_stack_map_by_type(int type)
vals[i] = rand();
if (type == QUEUE)
- strncpy(file, "./test_queue_map.bpf.o", sizeof(file));
+ strscpy(file, "./test_queue_map.bpf.o", sizeof(file));
else if (type == STACK)
- strncpy(file, "./test_stack_map.bpf.o", sizeof(file));
+ strscpy(file, "./test_stack_map.bpf.o", sizeof(file));
else
return;
diff --git a/tools/testing/selftests/bpf/prog_tests/skc_to_unix_sock.c b/tools/testing/selftests/bpf/prog_tests/skc_to_unix_sock.c
index 3eefdfed1db9..74f6d7404d1b 100644
--- a/tools/testing/selftests/bpf/prog_tests/skc_to_unix_sock.c
+++ b/tools/testing/selftests/bpf/prog_tests/skc_to_unix_sock.c
@@ -34,7 +34,7 @@ void test_skc_to_unix_sock(void)
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sun_family = AF_UNIX;
- strncpy(sockaddr.sun_path, sock_path, strlen(sock_path));
+ strscpy(sockaddr.sun_path, sock_path, sizeof(sockaddr.sun_path));
sockaddr.sun_path[0] = '\0';
err = bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
diff --git a/tools/testing/selftests/bpf/prog_tests/task_local_data.h b/tools/testing/selftests/bpf/prog_tests/task_local_data.h
index 0f86b9275cf9..26e6218a2dd6 100644
--- a/tools/testing/selftests/bpf/prog_tests/task_local_data.h
+++ b/tools/testing/selftests/bpf/prog_tests/task_local_data.h
@@ -262,7 +262,7 @@ static tld_key_t __tld_create_key(const char *name, size_t size, bool dyn_data)
if (!atomic_compare_exchange_strong(&tld_meta_p->cnt, &cnt, cnt + 1))
goto retry;
- strncpy(tld_meta_p->metadata[i].name, name, TLD_NAME_LEN);
+ strscpy(tld_meta_p->metadata[i].name, name, TLD_NAME_LEN);
atomic_store(&tld_meta_p->metadata[i].size, size);
return (tld_key_t){(__s16)off};
}
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
index 76d72a59365e..bd0010e5364f 100644
--- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
+++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
@@ -1095,7 +1095,7 @@ static int tun_open(char *name)
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (*name)
- strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ strscpy(ifr.ifr_name, name, IFNAMSIZ);
err = ioctl(fd, TUNSETIFF, &ifr);
if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index 02a85dda30e6..d1418ec1f351 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -1799,7 +1799,7 @@ static int worker_main_send_subtests(int sock, struct test_state *state)
msg.subtest_done.num = i;
- strncpy(msg.subtest_done.name, subtest_state->name, MAX_SUBTEST_NAME);
+ strscpy(msg.subtest_done.name, subtest_state->name, MAX_SUBTEST_NAME);
msg.subtest_done.error_cnt = subtest_state->error_cnt;
msg.subtest_done.skipped = subtest_state->skipped;
diff --git a/tools/testing/selftests/bpf/xdp_hw_metadata.c b/tools/testing/selftests/bpf/xdp_hw_metadata.c
index 3d8de0d4c96a..1de1bcdb2c9a 100644
--- a/tools/testing/selftests/bpf/xdp_hw_metadata.c
+++ b/tools/testing/selftests/bpf/xdp_hw_metadata.c
@@ -550,7 +550,7 @@ static int rxq_num(const char *ifname)
struct ifreq ifr = {
.ifr_data = (void *)&ch,
};
- strncpy(ifr.ifr_name, ifname, IF_NAMESIZE - 1);
+ strscpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
int fd, ret;
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
@@ -571,7 +571,7 @@ static void hwtstamp_ioctl(int op, const char *ifname, struct hwtstamp_config *c
struct ifreq ifr = {
.ifr_data = (void *)cfg,
};
- strncpy(ifr.ifr_name, ifname, IF_NAMESIZE - 1);
+ strscpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
int fd, ret;
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 5/7] selftests/bpf: Use strscpy_cat() in the test_loader
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
` (3 preceding siblings ...)
2026-02-20 18:20 ` [PATCH bpf v1 4/7] selftests/bpf: Replace strncpy() " Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 6/7] selftests/bpf: Use strscpy in bpftool_helpers.c Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 7/7] selftests/bpf: Use memcpy() for bounded non-NULL-terminated copies Ihor Solodrai
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
Replace a pair of strcpy() calls that construct the unpriv test name
with a single strscpy_cat() call. This simplifies the code and adds
bounds checking.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/test_loader.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 338c035c3688..af4319566fea 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -673,18 +673,18 @@ static int parse_test_spec(struct test_loader *tester,
if (spec->mode_mask & UNPRIV) {
int descr_len = strlen(description);
- const char *suffix = " @unpriv";
+ static const char suffix[] = " @unpriv";
+ int name_len = descr_len + sizeof(suffix) + 1;
char *name;
- name = malloc(descr_len + strlen(suffix) + 1);
+ name = malloc(name_len);
if (!name) {
PRINT_FAIL("failed to allocate memory for unpriv.name\n");
err = -ENOMEM;
goto cleanup;
}
- strcpy(name, description);
- strcpy(&name[descr_len], suffix);
+ strscpy_cat(name, name_len, description, suffix);
spec->unpriv.name = name;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 6/7] selftests/bpf: Use strscpy in bpftool_helpers.c
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
` (4 preceding siblings ...)
2026-02-20 18:20 ` [PATCH bpf v1 5/7] selftests/bpf: Use strscpy_cat() in the test_loader Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 7/7] selftests/bpf: Use memcpy() for bounded non-NULL-terminated copies Ihor Solodrai
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
Replace strncpy() and snprintf() calls in bpftool_helpers.c with
strscpy() and strscpy_cat() respectively.
Pass the destination buffer size to detect_bpftool_path() instead of
hardcoding BPFTOOL_PATH_MAX_LEN.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/bpftool_helpers.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/bpf/bpftool_helpers.c b/tools/testing/selftests/bpf/bpftool_helpers.c
index a5824945a4a5..c99776b03f52 100644
--- a/tools/testing/selftests/bpf/bpftool_helpers.c
+++ b/tools/testing/selftests/bpf/bpftool_helpers.c
@@ -1,33 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
-#include "bpftool_helpers.h"
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
+#include "bpf_util.h"
+#include "bpftool_helpers.h"
+
#define BPFTOOL_PATH_MAX_LEN 64
#define BPFTOOL_FULL_CMD_MAX_LEN 512
#define BPFTOOL_DEFAULT_PATH "tools/sbin/bpftool"
-static int detect_bpftool_path(char *buffer)
+static int detect_bpftool_path(char *buffer, size_t size)
{
char tmp[BPFTOOL_PATH_MAX_LEN];
/* Check default bpftool location (will work if we are running the
* default flavor of test_progs)
*/
- snprintf(tmp, BPFTOOL_PATH_MAX_LEN, "./%s", BPFTOOL_DEFAULT_PATH);
+ strscpy_cat(tmp, sizeof(tmp), "./", BPFTOOL_DEFAULT_PATH);
if (access(tmp, X_OK) == 0) {
- strncpy(buffer, tmp, BPFTOOL_PATH_MAX_LEN);
+ strscpy(buffer, tmp, size);
return 0;
}
/* Check alternate bpftool location (will work if we are running a
* specific flavor of test_progs, e.g. cpuv4 or no_alu32)
*/
- snprintf(tmp, BPFTOOL_PATH_MAX_LEN, "../%s", BPFTOOL_DEFAULT_PATH);
+ strscpy_cat(tmp, sizeof(tmp), "../", BPFTOOL_DEFAULT_PATH);
if (access(tmp, X_OK) == 0) {
- strncpy(buffer, tmp, BPFTOOL_PATH_MAX_LEN);
+ strscpy(buffer, tmp, size);
return 0;
}
@@ -44,7 +46,7 @@ static int run_command(char *args, char *output_buf, size_t output_max_len)
int ret;
/* Detect and cache bpftool binary location */
- if (bpftool_path[0] == 0 && detect_bpftool_path(bpftool_path))
+ if (bpftool_path[0] == 0 && detect_bpftool_path(bpftool_path, sizeof(bpftool_path)))
return 1;
ret = snprintf(command, BPFTOOL_FULL_CMD_MAX_LEN, "%s %s%s",
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v1 7/7] selftests/bpf: Use memcpy() for bounded non-NULL-terminated copies
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
` (5 preceding siblings ...)
2026-02-20 18:20 ` [PATCH bpf v1 6/7] selftests/bpf: Use strscpy in bpftool_helpers.c Ihor Solodrai
@ 2026-02-20 18:20 ` Ihor Solodrai
6 siblings, 0 replies; 9+ messages in thread
From: Ihor Solodrai @ 2026-02-20 18:20 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman
Cc: bpf, linux-kernel, kernel-team
Replace strncpy() with memcpy() in cases where the source is
non-NULL-terminated and the copy length is known.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c | 6 ++++--
tools/testing/selftests/bpf/test_verifier.c | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
index dd75ccb03770..469e92869523 100644
--- a/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
+++ b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
@@ -308,8 +308,10 @@ static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches
return -1;
}
- strncpy(type_str, type, type_sz);
- strncpy(field_str, field, field_sz);
+ memcpy(type_str, type, type_sz);
+ type_str[type_sz] = '\0';
+ memcpy(field_str, field, field_sz);
+ field_str[field_sz] = '\0';
btf_id = btf__find_by_name(btf, type_str);
if (btf_id < 0) {
PRINT_FAIL("No BTF info for type %s\n", type_str);
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 27db34ecf3f5..a8ae03c57bba 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -1320,7 +1320,7 @@ static bool cmp_str_seq(const char *log, const char *exp)
printf("FAIL\nTestcase bug\n");
return false;
}
- strncpy(needle, exp, len);
+ memcpy(needle, exp, len);
needle[len] = 0;
q = strstr(log, needle);
if (!q) {
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat()
2026-02-20 18:20 ` [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat() Ihor Solodrai
@ 2026-02-20 23:04 ` Alexei Starovoitov
0 siblings, 0 replies; 9+ messages in thread
From: Alexei Starovoitov @ 2026-02-20 23:04 UTC (permalink / raw)
To: Ihor Solodrai
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, bpf, LKML, Kernel Team
On Fri, Feb 20, 2026 at 10:20 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Add sized_strscpy_cat() to bpf_util.h, which concatenates multiple
> strings into a destination buffer with strscpy().
>
> Add strscpy_cat() macro with varargs.
>
> This is a convenient helper that provides the same guarantees as
> strscpy(), but for a case when multiple strings need to be
> concatenated into destination.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
> tools/testing/selftests/bpf/bpf_util.h | 28 ++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h
> index 8c95ef7ed7c0..61bb22c3e517 100644
> --- a/tools/testing/selftests/bpf/bpf_util.h
> +++ b/tools/testing/selftests/bpf/bpf_util.h
> @@ -52,6 +52,34 @@ static inline ssize_t sized_strscpy(char *dest, const char *src, size_t count)
> #undef strscpy /* Redefine the placeholder from tools/include/linux/string.h */
> #define strscpy sized_strscpy
>
> +/*
> + * strscpy() analogue that concatenates multiple strings into a buffer
> + */
> +static inline ssize_t sized_strscpy_cat(char *dest, size_t dest_sz,
> + const char * const *srcs, size_t n)
> +{
> + ssize_t pos = 0;
> +
> + if (dest_sz == 0)
> + return -E2BIG;
> +
> + for (size_t i = 0; i < n; i++) {
> + ssize_t res = strscpy(dest + pos, srcs[i], dest_sz - pos);
> +
> + if (res < 0)
> + return res;
> + pos += res;
> + }
> +
> + return pos;
> +}
> +
> +#define strscpy_cat(dest, count, ...) \
> + sized_strscpy_cat(dest, count, \
> + (const char * const[]){ __VA_ARGS__ }, \
> + ARRAY_SIZE(((const char * const[]){ __VA_ARGS__ })))
Interesting idea, but this is not bpf material anymore,
and api needs to be discussed.
Let's drop it for now and corresponding changes in later patches.
Re: patch 1.
I think we can do kernel's trick:
CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
We have ___bpf_apply and ___bpf_narg in bpf_helpers.h
so strscpy() can be 2 args and it will make patch 3 less churny.
Also combine both patch sets, since the ASAN one depends on this one.
pw-bot: cr
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-02-20 23:04 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-20 18:20 [PATCH bpf v1 0/7] selftests/bpf: Add and use strscpy() Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 1/7] selftests/bpf: Add simple strscpy() implementation Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 2/7] selftests/bpf: Add strscpy_cat() Ihor Solodrai
2026-02-20 23:04 ` Alexei Starovoitov
2026-02-20 18:20 ` [PATCH bpf v1 3/7] selftests/bpf: Replace strcpy() calls with strscpy() Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 4/7] selftests/bpf: Replace strncpy() " Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 5/7] selftests/bpf: Use strscpy_cat() in the test_loader Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 6/7] selftests/bpf: Use strscpy in bpftool_helpers.c Ihor Solodrai
2026-02-20 18:20 ` [PATCH bpf v1 7/7] selftests/bpf: Use memcpy() for bounded non-NULL-terminated copies Ihor Solodrai
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.