public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox