summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c2
-rw-r--r--tools/testing/selftests/drivers/net/config1
-rwxr-xr-xtools/testing/selftests/drivers/net/psp.py46
-rw-r--r--tools/testing/selftests/landlock/scoped_abstract_unix_test.c76
-rw-r--r--tools/testing/selftests/landlock/trace_fs_test.c160
-rw-r--r--tools/testing/selftests/net/Makefile1
-rw-r--r--tools/testing/selftests/net/af_unix/msg_oob.c67
-rw-r--r--tools/testing/selftests/net/af_unix/scm_rights.c17
-rwxr-xr-xtools/testing/selftests/net/cork_fragsize.py187
-rwxr-xr-xtools/testing/selftests/net/fib_nexthops.sh28
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_connect.c4
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_lib.sh26
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_rfc5961_reject-old-ack.pkt29
-rwxr-xr-xtools/testing/selftests/net/pmtu.sh2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c11
-rw-r--r--tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c4
-rw-r--r--tools/testing/selftests/powerpc/tm/tm.h8
-rw-r--r--tools/testing/selftests/riscv/cfi/cfi_rv_test.h2
-rw-r--r--tools/testing/selftests/riscv/hwprobe/hwprobe.c20
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json115
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json78
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/route.json210
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json72
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json4
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json72
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf_flows_limit.json128
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json204
-rw-r--r--tools/testing/selftests/ublk/Makefile2
-rwxr-xr-xtools/testing/selftests/ublk/test_recover_03.sh5
-rw-r--r--tools/testing/selftests/x86/Makefile2
-rw-r--r--tools/testing/selftests/x86/int_signal.c311
31 files changed, 1847 insertions, 47 deletions
diff --git a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
index d23f154d3288..5d9dc8bcfbf5 100644
--- a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
+++ b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
@@ -69,7 +69,7 @@ fail:
int execute_test(pid_t pid)
{
pthread_t thread_id[MAX_THREADS];
- int thread_data[MAX_THREADS];
+ intptr_t thread_data[MAX_THREADS];
for (int i = 0; i < MAX_THREADS; i++)
pthread_create(&thread_id[i], NULL,
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index b6989c7d3d9d..4838adf27fa1 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -21,5 +21,6 @@ CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_SCH_PRIO=m
CONFIG_PPP=y
CONFIG_PPPOE=y
+CONFIG_TLS=y
CONFIG_VLAN_8021Q=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py
index 315648a770d0..a5b1e14f120f 100755
--- a/tools/testing/selftests/drivers/net/psp.py
+++ b/tools/testing/selftests/drivers/net/psp.py
@@ -23,6 +23,8 @@ from lib.py import NetNSEnter
from lib.py import bkg, rand_port, wait_port_listen
from lib.py import ip
+TCP_ULP = 31
+
def _get_outq(s):
one = b'\0' * 4
@@ -333,6 +335,50 @@ def assoc_version_mismatch(cfg):
ksft_eq(the_exception.nl_msg.error, -errno.EINVAL)
+def _require_tls_ulp():
+ with socket.create_server(("localhost", 0)) as srv, \
+ socket.create_connection(srv.getsockname()) as s:
+ try:
+ s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls")
+ except OSError as exc:
+ raise KsftSkipEx("kTLS not available") from exc
+
+
+def assoc_psp_ulp_exclusive(cfg):
+ """ Test that a TCP ULP cannot be attached to a PSP socket """
+ _init_psp_dev(cfg)
+ _require_tls_ulp()
+
+ with _make_clr_conn(cfg) as s:
+ try:
+ cfg.pspnl.rx_assoc({"version": 0,
+ "dev-id": cfg.psp_dev_id,
+ "sock-fd": s.fileno()})
+ with ksft_raises(OSError) as cm:
+ s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls")
+ ksft_eq(cm.exception.errno, errno.EINVAL)
+ finally:
+ _close_conn(cfg, s)
+
+
+def assoc_ulp_psp_exclusive(cfg):
+ """ Test that a PSP assoc cannot be added to a socket with a TCP ULP """
+ _init_psp_dev(cfg)
+ _require_tls_ulp()
+
+ with _make_clr_conn(cfg) as s:
+ try:
+ s.setsockopt(socket.SOL_TCP, TCP_ULP, b"tls")
+ with ksft_raises(NlError) as cm:
+ cfg.pspnl.rx_assoc({"version": 0,
+ "dev-id": cfg.psp_dev_id,
+ "sock-fd": s.fileno()})
+ ksft_eq(cm.exception.nl_msg.error, -errno.EINVAL)
+ ksft_eq(cm.exception.nl_msg.extack['bad-attr'], ".sock-fd")
+ finally:
+ _close_conn(cfg, s)
+
+
def assoc_twice(cfg):
""" Test reusing Tx assoc for two sockets """
_init_psp_dev(cfg)
diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
index 6dbe863ea571..5dc0debacb2a 100644
--- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
+++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c
@@ -1222,7 +1222,7 @@ FIXTURE_SETUP(trace_unix)
int ret;
set_cap(_metadata, CAP_SYS_ADMIN);
- ASSERT_EQ(0, unshare(CLONE_NEWNS));
+ ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWNET));
ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
ret = tracefs_fixture_setup();
@@ -1252,6 +1252,11 @@ FIXTURE_TEARDOWN(trace_unix)
clear_cap(_metadata, CAP_SYS_ADMIN);
}
+static const char
+ trace_unix_max_name[sizeof(((struct sockaddr_un *)0)->sun_path)] = {
+ [0 ... sizeof(trace_unix_max_name) - 2] = 'x',
+ };
+
/* clang-format off */
FIXTURE_VARIANT(trace_unix) {
/* clang-format on */
@@ -1259,6 +1264,8 @@ FIXTURE_VARIANT(trace_unix) {
bool sandbox;
bool sandbox_target; /* Peer owned by a domain: peer_domain != 0. */
int expect_denied;
+ const char *name; /* NULL generates a PID-based binary name. */
+ size_t name_len;
};
/* clang-format off */
@@ -1281,6 +1288,26 @@ FIXTURE_VARIANT_ADD(trace_unix, stream_allowed) {
.sandbox_target = false, .expect_denied = 0,
};
+/* Stream: lower abstract-name length boundary. */
+FIXTURE_VARIANT_ADD(trace_unix, stream_denied_empty_name) {
+ .sock_type = SOCK_STREAM,
+ .sandbox = true,
+ .sandbox_target = false,
+ .expect_denied = 1,
+ .name = "",
+ .name_len = 0,
+};
+
+/* Stream: upper abstract-name length boundary. */
+FIXTURE_VARIANT_ADD(trace_unix, stream_denied_max_name) {
+ .sock_type = SOCK_STREAM,
+ .sandbox = true,
+ .sandbox_target = false,
+ .expect_denied = 1,
+ .name = trace_unix_max_name,
+ .name_len = sizeof(trace_unix_max_name) - 1,
+};
+
/* Datagram: sandboxed client sendto() an unsandboxed peer (peer_domain=0). */
FIXTURE_VARIANT_ADD(trace_unix, dgram_denied) {
.sock_type = SOCK_DGRAM, .sandbox = true,
@@ -1304,12 +1331,11 @@ FIXTURE_VARIANT_ADD(trace_unix, dgram_allowed) {
/*
* A sandboxed thread reaching an abstract unix socket peer through connect(2)
* (stream) or sendto(2) (datagram) is denied and emits
- * landlock_deny_scope_abstract_unix_socket. The abstract name is crafted with
- * a space and an embedded NUL followed by an "END" marker to check the
- * tracepoint escaping and its length handling (a raw space would break the
- * sun_path field regex; strlen() would truncate at the NUL and drop "END").
- * peer_pid is only meaningful for a stream peer (a datagram peer has no
- * SO_PEERCRED), so it is asserted only there.
+ * landlock_deny_scope_abstract_unix_socket. The default abstract name has a
+ * space and an embedded NUL followed by an "END" marker to check escaping and
+ * binary length handling. Additional stream variants cover the minimum and
+ * maximum abstract-name lengths. peer_pid is only meaningful for a stream peer
+ * (a datagram peer has no SO_PEERCRED), so it is asserted only there.
*/
TEST_F(trace_unix, deny_scope_unix)
{
@@ -1336,12 +1362,19 @@ TEST_F(trace_unix, deny_scope_unix)
ASSERT_LE(0, server_fd);
addr.sun_path[0] = '\0';
- name_len = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1,
- "landlock_trace_test_%d ", getpid());
- addr.sun_path[1 + name_len] = '\0';
- memcpy(addr.sun_path + 1 + name_len + 1, "END", 3);
- addr_len =
- offsetof(struct sockaddr_un, sun_path) + 1 + name_len + 1 + 3;
+ if (variant->name) {
+ ASSERT_LE(variant->name_len, sizeof(addr.sun_path) - 1);
+ memcpy(addr.sun_path + 1, variant->name, variant->name_len);
+ name_len = variant->name_len;
+ } else {
+ name_len = snprintf(addr.sun_path + 1,
+ sizeof(addr.sun_path) - 1,
+ "landlock_trace_test_%d ", getpid());
+ addr.sun_path[1 + name_len] = '\0';
+ memcpy(addr.sun_path + 1 + name_len + 1, "END", 3);
+ name_len += 1 + 3;
+ }
+ addr_len = offsetof(struct sockaddr_un, sun_path) + 1 + name_len;
ASSERT_EQ(0, bind(server_fd, (struct sockaddr *)&addr, addr_len));
if (variant->sock_type == SOCK_STREAM)
@@ -1430,19 +1463,18 @@ TEST_F(trace_unix, deny_scope_unix)
count, buf);
}
- /*
- * sun_path is escaped: a raw space would break this field's [^ ]*$
- * regex, so a successful extract proves the space was escaped, and its
- * full length is honored: the "END" marker after the embedded NUL must
- * survive (strlen() would truncate it at the NUL).
- */
ASSERT_EQ(0, tracefs_extract_field(
buf,
REGEX_DENY_SCOPE_ABSTRACT_UNIX_SOCKET(TRACE_TASK),
"sun_path", field, sizeof(field)));
- EXPECT_NE(NULL, strstr(field, "END"))
- {
- TH_LOG("sun_path truncated or unescaped: %s", field);
+ if (variant->name) {
+ EXPECT_STREQ(variant->name, field);
+ } else {
+ /* An embedded NUL must not truncate the following marker. */
+ EXPECT_NE(NULL, strstr(field, "END"))
+ {
+ TH_LOG("sun_path truncated or unescaped: %s", field);
+ }
}
/* peer_pid is the parent's PID for a stream peer (0 for datagram). */
diff --git a/tools/testing/selftests/landlock/trace_fs_test.c b/tools/testing/selftests/landlock/trace_fs_test.c
index 5220f6a4bee1..4543a25c1f55 100644
--- a/tools/testing/selftests/landlock/trace_fs_test.c
+++ b/tools/testing/selftests/landlock/trace_fs_test.c
@@ -6,8 +6,10 @@
*/
#define _GNU_SOURCE
+#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <linux/landlock.h>
#include <sched.h>
#include <stdio.h>
@@ -23,6 +25,63 @@
#define TRACE_TASK "trace_fs_test"
+/* Mirrors TRACE_SEQ_SIZE, conservatively larger than the usable buffer. */
+#define TRACE_SEQUENCE_SIZE 8192
+#define OCTAL_ESCAPE_LEN 4
+#define LONG_PATH_COMPONENT_COUNT 11
+#define LONG_PATH_COMPONENT_LEN 240
+#define LONG_PATH_LEN \
+ (LONG_PATH_COMPONENT_COUNT * (LONG_PATH_COMPONENT_LEN + 1) + \
+ sizeof("/tmp"))
+#define LONG_ESCAPED_PATH_LEN \
+ (LONG_PATH_COMPONENT_COUNT * LONG_PATH_COMPONENT_LEN * OCTAL_ESCAPE_LEN)
+
+static_assert(LONG_ESCAPED_PATH_LEN > TRACE_SEQUENCE_SIZE,
+ "escaped path must exceed the trace sequence");
+static_assert(LONG_PATH_LEN < PATH_MAX, "path must fit in PATH_MAX");
+
+static void create_long_path(struct __test_metadata *const _metadata,
+ char *path)
+{
+ size_t path_len;
+
+ strcpy(path, "/tmp");
+ path_len = strlen(path);
+
+ set_cap(_metadata, CAP_SYS_ADMIN);
+ ASSERT_EQ(0, mount("tmpfs", "/tmp", "tmpfs", 0, NULL));
+ clear_cap(_metadata, CAP_SYS_ADMIN);
+
+ for (int i = 0; i < LONG_PATH_COMPONENT_COUNT; i++) {
+ path[path_len++] = '/';
+ memset(path + path_len, ' ', LONG_PATH_COMPONENT_LEN);
+ path_len += LONG_PATH_COMPONENT_LEN;
+ path[path_len] = '\0';
+ ASSERT_EQ(0, mkdir(path, 0700));
+ }
+}
+
+static void expect_truncated_path(struct __test_metadata *const _metadata,
+ const char *const trace,
+ const char *const event_regex)
+{
+ static const char marker[] = "\xe2\x80\xa6";
+ char *path;
+ size_t path_len;
+
+ path = malloc(TRACE_SEQUENCE_SIZE);
+ ASSERT_NE(NULL, path);
+ ASSERT_EQ(0, tracefs_extract_field(trace, event_regex, "path", path,
+ TRACE_SEQUENCE_SIZE));
+ EXPECT_EQ(path, strstr(path, "/tmp/"));
+ EXPECT_NE(NULL, strstr(path, "\\040"));
+
+ path_len = strlen(path);
+ ASSERT_LE(sizeof(marker) - 1, path_len);
+ EXPECT_STREQ(marker, path + path_len - (sizeof(marker) - 1));
+ free(path);
+}
+
/*
* Like REGEX_DENY_ACCESS_FS(), but pins the logged field to a specific value
* ("0" or "1") so a test can tell a suppressed (quiet) denial from a logged
@@ -184,6 +243,107 @@ TEST_F(trace_fs, add_rule_fs)
}
/*
+ * Verifies that a path whose escaping exceeds the trace scratch sequence does
+ * not corrupt a sibling symbolic field.
+ */
+TEST_F(trace_fs, add_rule_fs_escaped_path_overflow)
+{
+ static const char access_prefix[] = "execute|write_file|read_file|";
+ static const char access_suffix[] = "|ioctl_dev|resolve_unix";
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
+ };
+ struct landlock_path_beneath_attr path_beneath = {
+ .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
+ };
+ char path[PATH_MAX];
+ char *buf, field_buf[256];
+ size_t field_len;
+ int ruleset_fd, count;
+
+ create_long_path(_metadata, path);
+
+ ruleset_fd =
+ landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+ path_beneath.parent_fd = open(path, O_PATH | O_DIRECTORY | O_CLOEXEC);
+ ASSERT_LE(0, path_beneath.parent_fd);
+
+ ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
+ &path_beneath, 0));
+ ASSERT_EQ(0, close(path_beneath.parent_fd));
+ ASSERT_EQ(0, close(ruleset_fd));
+
+ buf = tracefs_read_buf();
+ ASSERT_NE(NULL, buf);
+
+ count = tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK));
+ EXPECT_EQ(1, count)
+ {
+ TH_LOG("Expected 1 add_rule_fs event, got %d\n%s", count, buf);
+ }
+
+ /*
+ * The marker catches a full revert with any compiler. The symbolic
+ * field also catches scratch-sequence poisoning when the compiler
+ * evaluates the overflowing path first, as GCC currently does.
+ */
+ ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK),
+ "access_rights", field_buf,
+ sizeof(field_buf)));
+ EXPECT_EQ(0,
+ strncmp(field_buf, access_prefix, sizeof(access_prefix) - 1));
+ EXPECT_EQ(NULL, strstr(field_buf, "|refer|"));
+ field_len = strlen(field_buf);
+ ASSERT_LE(sizeof(access_suffix) - 1, field_len);
+ EXPECT_STREQ(access_suffix,
+ field_buf + field_len - (sizeof(access_suffix) - 1));
+ expect_truncated_path(_metadata, buf, REGEX_ADD_RULE_FS(TRACE_TASK));
+
+ free(buf);
+}
+
+/*
+ * Verifies that an overflowing denied path does not corrupt its sibling
+ * symbolic blockers field.
+ */
+TEST_F(trace_fs, deny_access_fs_escaped_path_overflow)
+{
+ char path[PATH_MAX];
+ char *buf, field_buf[64];
+ int count, err;
+
+ create_long_path(_metadata, path);
+ ASSERT_EQ(0, tracefs_clear_buf());
+
+ sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR,
+ LANDLOCK_ACCESS_FS_READ_DIR, path);
+
+ buf = tracefs_read_buf();
+ ASSERT_NE(NULL, buf);
+
+ count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
+ EXPECT_EQ(1, count)
+ {
+ TH_LOG("Expected 1 deny_access_fs event, got %d\n%s", count,
+ buf);
+ }
+
+ /*
+ * The marker catches a full revert with any compiler. The symbolic
+ * field also catches scratch-sequence poisoning when the compiler
+ * evaluates the overflowing path first, as GCC currently does.
+ */
+ err = tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK),
+ "blockers", field_buf, sizeof(field_buf));
+ ASSERT_EQ(0, err);
+ EXPECT_STREQ("read_dir", field_buf);
+ expect_truncated_path(_metadata, buf, REGEX_DENY_ACCESS_FS(TRACE_TASK));
+
+ free(buf);
+}
+
+/*
* Verifies that an allowed access emits check_rule events (rule matched during
* pathwalk) but does NOT emit deny_access events (no denial).
*/
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 517c09d60bef..3ee3378f8b26 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -25,6 +25,7 @@ TEST_PROGS := \
cmsg_so_mark.sh \
cmsg_so_priority.sh \
cmsg_time.sh \
+ cork_fragsize.py \
double_udp_encap.sh \
drop_monitor_tests.sh \
ecmp_rehash.sh \
diff --git a/tools/testing/selftests/net/af_unix/msg_oob.c b/tools/testing/selftests/net/af_unix/msg_oob.c
index 1b499d56656c..f051d79f7a8e 100644
--- a/tools/testing/selftests/net/af_unix/msg_oob.c
+++ b/tools/testing/selftests/net/af_unix/msg_oob.c
@@ -290,6 +290,25 @@ static void __setinlinepair(struct __test_metadata *_metadata,
}
}
+static void __setblockingpair(struct __test_metadata *_metadata,
+ FIXTURE_DATA(msg_oob) *self)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int ret, old_flags, flags;
+
+ old_flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0);
+ ASSERT_NE(-1, old_flags);
+
+ ret = fcntl(self->fd[i * 2 + 1], F_SETFL, old_flags & ~O_NONBLOCK);
+ ASSERT_EQ(0, ret);
+
+ flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0);
+ ASSERT_EQ(old_flags & ~O_NONBLOCK, flags);
+ }
+}
+
static void __siocatmarkpair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self,
bool oob_head)
@@ -347,6 +366,9 @@ static void __resetpair(struct __test_metadata *_metadata,
#define setinlinepair() \
__setinlinepair(_metadata, self)
+#define setblockingpair() \
+ __setblockingpair(_metadata, self)
+
#define resetpair(reset) \
__resetpair(_metadata, self, variant, reset)
@@ -888,4 +910,49 @@ TEST_F(msg_oob, inline_ex_oob_siocatmark)
resetpair(true);
}
+TEST_F(msg_oob, zero_buf_oob)
+{
+ sendpair("a", 1, MSG_OOB);
+ recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_oob_blocking)
+{
+ sendpair("a", 1, MSG_OOB);
+ setblockingpair();
+ recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_non_oob_oob)
+{
+ sendpair("ab", 2, MSG_OOB);
+ recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_non_oob_oob_blocking)
+{
+ sendpair("ab", 2, MSG_OOB);
+ setblockingpair();
+ recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_ex_oob_oob)
+{
+ sendpair("a", 1, MSG_OOB);
+ recvpair("a", 1, 1, MSG_OOB);
+
+ sendpair("b", 1, MSG_OOB);
+ recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_ex_oob_oob_blocking)
+{
+ sendpair("a", 1, MSG_OOB);
+ recvpair("a", 1, 1, MSG_OOB);
+
+ sendpair("b", 1, MSG_OOB);
+ setblockingpair();
+ recvpair("", 0, 0, 0);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/af_unix/scm_rights.c b/tools/testing/selftests/net/af_unix/scm_rights.c
index d82a79c21c17..c165f250220a 100644
--- a/tools/testing/selftests/net/af_unix/scm_rights.c
+++ b/tools/testing/selftests/net/af_unix/scm_rights.c
@@ -378,4 +378,21 @@ TEST_F(scm_rights, backtrack_from_scc)
close_sockets(10);
}
+TEST_F(scm_rights, mixed_lowpoint)
+{
+ create_sockets(6);
+
+ send_fd(0, 1);
+ send_fd(1, 2);
+ send_fd(2, 1);
+ send_fd(1, 0);
+
+ send_fd(3, 4);
+ send_fd(4, 5);
+ send_fd(5, 4);
+ send_fd(4, 3);
+
+ close_sockets(6);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/cork_fragsize.py b/tools/testing/selftests/net/cork_fragsize.py
new file mode 100755
index 000000000000..7afd643d07ec
--- /dev/null
+++ b/tools/testing/selftests/net/cork_fragsize.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+'''Test possible UDP length overflow in udp_send_skb/udp_v6_send_skb.'''
+
+import errno
+import gzip
+import os
+import socket
+import struct
+import subprocess
+from contextlib import contextmanager
+
+from lib.py import (
+ KsftNamedVariant,
+ KsftSkipEx,
+ NetNS,
+ NetNSEnter,
+ defer,
+ ip,
+ ksft_eq,
+ ksft_exit,
+ ksft_pr,
+ ksft_raises,
+ ksft_run,
+ ksft_true,
+ ksft_variants,
+)
+
+IP_MTU_DISCOVER = 10
+IP_PMTUDISC_PROBE = 3
+IPV6_MTU_DISCOVER = 23
+IPV6_PMTUDISC_DO = 2
+IPV6_PMTUDISC_PROBE = 3
+IPV6_TLV_JUMBO = 194
+
+
+def check_kernel_config(option: str) -> bool | None:
+ '''
+ Check whether the option is enabled in the config of the running kernel.
+ Returns None if the config is not found; otherwise returns True/False
+ depending on the option value in the config.
+ '''
+
+ for filename, method in [
+ ('/proc/config.gz', gzip.open),
+ (f'/boot/config-{os.uname().release}', open),
+ ]:
+ try:
+ with method(filename, 'rt') as config:
+ for line in config:
+ if line.rstrip() == f'{option}=y':
+ return True
+ return False
+ except OSError:
+ continue
+ return None
+
+
+def assert_debug_kernel() -> None:
+ '''
+ Skip the test if CONFIG_DEBUG_NET is not set in the kernel config.
+ '''
+
+ res = check_kernel_config('CONFIG_DEBUG_NET')
+ if res is None:
+ ksft_pr("WARN: Can't read kernel config; assuming debug kernel, and running the test")
+ elif not res:
+ raise KsftSkipEx('CONFIG_DEBUG_NET is not set')
+
+
+def check_dmesg_clean(func: str) -> bool:
+ '''
+ Check if the given function produced a WARN in dmesg.
+ '''
+
+ with subprocess.Popen(['dmesg'], stdout=subprocess.PIPE) as dmesg:
+ res = subprocess.run(['grep', '-q', f'WARNING:.*{func}'], stdin=dmesg.stdout, check=False)
+ return res.returncode != 0 and dmesg.returncode == 0
+
+
+@contextmanager
+def dummy_netdev(ns: NetNS, mtu: int, ipv6: bool) -> None:
+ '''
+ Create a dummy netdev inside the given namespace, and tune it for the test.
+ '''
+
+ ip('link add dummy type dummy', ns=ns)
+ with defer(ip, 'link del dummy', ns=ns):
+ ip(f'link set dummy mtu {mtu}', ns=ns)
+ ip('link set dummy up', ns=ns)
+ flag = '-6' if ipv6 else ''
+ nodad = 'nodad' if ipv6 else ''
+ local = 'fd00::1/64' if ipv6 else '10.0.0.1/24'
+ remote = 'fd00::2' if ipv6 else '10.0.0.2'
+ ip(f'{flag} addr add {local} dev dummy {nodad}', ns=ns)
+ ip(f'{flag} neigh add {remote} lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
+ yield
+
+
+@ksft_variants([
+ KsftNamedVariant(
+ 'ipv6',
+ True,
+ socket.AF_INET6,
+ (socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO),
+ 'fd00::2',
+ 'udp_v6_send_skb',
+ ),
+ KsftNamedVariant(
+ 'ipv4',
+ False,
+ socket.AF_INET,
+ (socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_PROBE),
+ '10.0.0.2',
+ 'udp_send_skb',
+ ),
+])
+def test_udp(
+ ipv6: bool,
+ af: socket.AddressFamily,
+ sockopts: tuple[int, int, int],
+ destip: str,
+ func: str
+) -> None:
+ '''
+ Test that sending an oversized UDP packet over a UDP socket doesn't overflow
+ the 16-bit length field in the UDP header, which could happen on older
+ kernels in udp_send_skb/udp_v6_send_skb.
+
+ IPv4: The packet will be dropped with EMSGSIZE, but the overflow could
+ happen before it happens. The only way to test this is to check dmesg on
+ CONFIG_DEBUG_NET=y kernels that have udp_set_len_short with the warning.
+
+ IPv6: The packet will be dropped with EMSGSIZE on fixed kernels, and will be
+ sent corrupted on older kernels. Test both: sendto must return EMSGSIZE, and
+ dmesg must be clean of warnings on CONFIG_DEBUG_NET=y kernels.
+ '''
+
+ if not ipv6:
+ assert_debug_kernel()
+
+ with (
+ NetNS() as ns,
+ dummy_netdev(ns, 65556 + 20 * ipv6, ipv6),
+ NetNSEnter(ns),
+ socket.socket(af, socket.SOCK_DGRAM) as fd,
+ ):
+ fd.setsockopt(*sockopts)
+ with ksft_raises(OSError) as e:
+ fd.sendto(b' ' * 65528, (destip, 1234))
+ # IPv6: EMSGSIZE happens on kernels with the fix.
+ # IPv4: EMSGSIZE happens on both fixed and unfixed kernels, after the
+ # WARN is printed - ignore it and rely on the dmesg check.
+ if e.exception is not None:
+ ksft_eq(e.exception.errno, errno.EMSGSIZE)
+
+ ksft_true(check_dmesg_clean(func), 'WARNING detected in dmesg')
+
+
+def test_ipv6_jumbo() -> None:
+ '''
+ Test that sending UDP jumbograms over a raw IPv6 socket works, despite
+ having the fix for oversized UDP packets. sendto must not raise an OSError
+ exception (when raised, the test fails automatically).
+ '''
+
+ with (
+ NetNS() as ns,
+ dummy_netdev(ns, 65584, True),
+ NetNSEnter(ns),
+ socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_UDP) as fd,
+ ):
+ hopopts = struct.pack('!BBBBI', 0, 0, IPV6_TLV_JUMBO, 4, 65544)
+ fd.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_HOPOPTS, hopopts)
+ fd.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_CHECKSUM, 6)
+ fd.setsockopt(socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_PROBE)
+ udp = struct.pack('!HHHH', 1234, 1234, 0, 0) + b' ' * 65528
+ fd.sendto(udp, ('fd00::2', 0))
+
+
+if __name__ == "__main__":
+ ksft_run([
+ test_udp,
+ test_ipv6_jumbo,
+ ])
+ ksft_exit()
diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh
index 3d347126730a..431d7bed7622 100755
--- a/tools/testing/selftests/net/fib_nexthops.sh
+++ b/tools/testing/selftests/net/fib_nexthops.sh
@@ -533,6 +533,20 @@ ipv6_fdb_grp_fcnal()
run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 61 self"
log_test $? 255 "Fdb mac add with nexthop"
+ # fdb entries with a nexthop group cannot be aged out
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static"
+ log_test $? 0 "Fdb mac add with nexthop group and static state"
+
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic"
+ log_test $? 255 "Fdb mac add with nexthop group and dynamic state"
+
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self"
+ run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic"
+ log_test $? 255 "Fdb mac replace with nexthop group and dynamic state"
+
+ run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic"
+ log_test $? 255 "Fdb mac append with nexthop group and dynamic state"
+
run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 66"
log_test $? 2 "Route add with fdb nexthop"
@@ -669,6 +683,20 @@ ipv4_fdb_grp_fcnal()
run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 12 self"
log_test $? 255 "Fdb mac add with nexthop"
+ # fdb entries with a nexthop group cannot be aged out
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static"
+ log_test $? 0 "Fdb mac add with nexthop group and static state"
+
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic"
+ log_test $? 255 "Fdb mac add with nexthop group and dynamic state"
+
+ run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self"
+ run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic"
+ log_test $? 255 "Fdb mac replace with nexthop group and dynamic state"
+
+ run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic"
+ log_test $? 255 "Fdb mac append with nexthop group and dynamic state"
+
run_cmd "$IP ro add 172.16.0.0/22 nhid 16"
log_test $? 2 "Route add with fdb nexthop"
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index ea4cb6c1bd5e..178d98d91fea 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -381,6 +381,9 @@ static int sock_connect_mptcp(const char * const remoteaddr,
hints.ai_family = pf;
+ /* Keep the resolved address alive for the whole execution: it is
+ * used again when reconnecting, and will be released at exit time.
+ */
xgetaddrinfo(remoteaddr, port, &hints, &addr);
for (a = addr; a; a = a->ai_next) {
sock = socket(a->ai_family, a->ai_socktype, proto);
@@ -421,7 +424,6 @@ static int sock_connect_mptcp(const char * const remoteaddr,
sock = -1;
}
- freeaddrinfo(addr);
if (sock != -1)
SOCK_TEST_TCPULP(sock, proto);
return sock;
diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
index 5ef6033775c8..b9d14647f401 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
@@ -108,12 +108,14 @@ mptcp_lib_pr_info() {
mptcp_lib_pr_nstat() {
local ns="${1}"
- local hist="/tmp/${ns}.out"
+ local cache="/tmp/${ns}.out"
+ local hist="/tmp/${ns}.nstat"
- if [ -f "${hist}" ]; then
- awk '$2 != 0 { print " "$0 }' "${hist}"
+ if [ -f "${cache}" ]; then
+ awk '$2 != 0 { print " "$0 }' "${cache}"
else
- ip netns exec "${ns}" nstat -as | grep Tcp
+ NSTAT_HISTORY="${hist}" ip netns exec "${ns}" nstat -s |
+ grep Tcp
fi
}
@@ -414,19 +416,21 @@ mptcp_lib_nstat_get() {
}
# $1: ns, $2: MIB counter
-# Get the counter from the history (mptcp_lib_nstat_{init,get}()) if available.
-# If not, get the counter from nstat ignoring any history.
+# Get the counter from the cache (mptcp_lib_nstat_{init,get}()) if available.
+# If not, get the counter from nstat ignoring any cache, but using the history.
mptcp_lib_get_counter() {
local ns="${1}"
local counter="${2}"
- local hist="/tmp/${ns}.out"
+ local cache="/tmp/${ns}.out"
+ local hist="/tmp/${ns}.nstat"
local count
- if [[ -s "${hist}" && "${counter}" == *"Tcp"* ]]; then
- count=$(awk "/^${counter} / {print \$2; exit}" "${hist}")
+ if [[ -s "${cache}" && "${counter}" == *"Tcp"* ]]; then
+ count=$(awk "/^${counter} / {print \$2; exit}" "${cache}")
else
- count=$(ip netns exec "${ns}" nstat -asz "${counter}" |
- awk 'NR==1 {next} {print $2}')
+ count=$(NSTAT_HISTORY="${hist}" ip netns exec "${ns}" \
+ nstat -sz "${counter}" |
+ awk 'NR==1 {next} {print $2}')
fi
if [ -z "${count}" ]; then
mptcp_lib_fail_if_expected_feature "${counter} counter"
diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_reject-old-ack.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_reject-old-ack.pkt
new file mode 100644
index 000000000000..32dd9de1d366
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_reject-old-ack.pkt
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+
+`./defaults.sh
+sysctl -q net.ipv4.tcp_invalid_ratelimit=0
+`
+
+// Test rejection of data segments carrying excessively old ACKs
+
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
++0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
++0 bind(3, ..., ...) = 0
++0 listen(3, 1024) = 0
+
+// ---------------- Handshake ------------------- //
++0 < S 0:0(0) win 65535
++0 > S. 0:0(0) ack 1 <...>
++0 < . 1:1(0) ack 1 win 65535
++0 accept(3, ..., ...) = 4
+
+// Populate receive memory so the following segment can use
+// header prediction.
++0 < P. 1:501(500) ack 1 win 65535
++0 > . 1:1(0) ack 501
+
+// Send an in-sequence data segment carrying an excessively old ACK.
++0 < P. 501:1501(1000) ack 2794967397 win 65535
+
+// Challenge ACK; RCV.NXT must remain 501.
++0 > . 1:1(0) ack 501
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
index a3323c21f001..c7cd271714ef 100755
--- a/tools/testing/selftests/net/pmtu.sh
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -1457,7 +1457,7 @@ test_pmtu_ipvX_over_bridged_vxlanY_or_geneveY_exception() {
mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1
- run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s $((${ll_mtu} + 500)) ${dst} || return 1
+ run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1
# Check that exceptions were created
pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
index ba2681a12cc7..9be5945f3b1f 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
+#include <time.h>
#include "ebb.h"
@@ -22,6 +23,7 @@ static int test_body(void)
{
int i, orig_period, max_period;
struct event event;
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1 };
SKIP_IF(!ebb_is_supported());
@@ -57,10 +59,15 @@ static int test_body(void)
* kernel to decide our timeslice is up and context switch to
* the other thread. When we come back our EBB will have been
* lost and we'll spin in this while loop forever.
+ *
+ * Use nanosleep(0) instead of sched_yield() to guarantee a
+ * context switch to the eat_cpu child regardless of the
+ * eligibility state. sched_yield() via yield_task_fair() may
+ * become a no-op when the task is ineligible (vruntime ahead
+ * of avg_vruntime), preventing the required context switch.
*/
-
for (i = 0; i < 100000; i++)
- sched_yield();
+ nanosleep(&ts, NULL);
/* Change the sample period slightly to try and hit the race */
if (sample_period >= (orig_period + 200))
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c
index 0a4bc479ae39..5dc0f12f467d 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c
@@ -60,7 +60,7 @@ void usr_signal_handler(int signo, siginfo_t *si, void *uc)
ucp->uc_link = mmap(NULL, sizeof(ucontext_t),
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
- if (ucp->uc_link == (void *)-1) {
+ if (ucp->uc_link == MAP_FAILED) {
perror("Mmap failed");
exit(-1);
}
@@ -129,7 +129,7 @@ void tm_trap_test(void)
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
- if (ss.ss_sp == (void *)-1) {
+ if (ss.ss_sp == MAP_FAILED) {
perror("mmap error\n");
exit(-1);
}
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h
index c03c6e778876..6024ce4ba6ff 100644
--- a/tools/testing/selftests/powerpc/tm/tm.h
+++ b/tools/testing/selftests/powerpc/tm/tm.h
@@ -105,8 +105,12 @@ static inline bool failure_is_nesting(void)
static inline int tcheck(void)
{
long cr;
- asm volatile ("tcheck 0" : "=r"(cr) : : "cr0");
- return (cr >> 28) & 4;
+ asm volatile("tcheck 0;"
+ "mfcr %0;"
+ : "=r"(cr)
+ :
+ : "cr0");
+ return (cr >> 28) & 0xf;
}
static inline bool tcheck_doomed(void)
diff --git a/tools/testing/selftests/riscv/cfi/cfi_rv_test.h b/tools/testing/selftests/riscv/cfi/cfi_rv_test.h
index 1c8043f2b778..184df6903d01 100644
--- a/tools/testing/selftests/riscv/cfi/cfi_rv_test.h
+++ b/tools/testing/selftests/riscv/cfi/cfi_rv_test.h
@@ -56,7 +56,7 @@
#define CSR_SSP 0x011
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#define __ASM_STR(x) x
#else
#define __ASM_STR(x) #x
diff --git a/tools/testing/selftests/riscv/hwprobe/hwprobe.c b/tools/testing/selftests/riscv/hwprobe/hwprobe.c
index 54c435af9923..eca4441ee77f 100644
--- a/tools/testing/selftests/riscv/hwprobe/hwprobe.c
+++ b/tools/testing/selftests/riscv/hwprobe/hwprobe.c
@@ -9,7 +9,7 @@ int main(int argc, char **argv)
long out;
ksft_print_header();
- ksft_set_plan(5);
+ ksft_set_plan(6);
/* Fake the CPU_SET ops. */
cpus = -1;
@@ -62,5 +62,23 @@ int main(int argc, char **argv)
pairs[1].key == 1 && pairs[1].value != 0xAAAA,
"Unknown key overwritten with -1 and doesn't block other elements\n");
+ pairs[0].key = RISCV_HWPROBE_KEY_IMA_EXT_0;
+ out = riscv_hwprobe(pairs, 1, 0, 0, 0);
+ if (out != 0)
+ ksft_exit_fail_msg("hwprobe(IMA_EXT_0) failed with %ld\n", out);
+
+ /*
+ * The RISC-V ISA manual specifies that Zfh implies Zfhmin and Zvfh
+ * implies Zvfhmin, so hwprobe must report the implied subset
+ * extensions whenever the supersets are present.
+ */
+ if ((pairs[0].value & RISCV_HWPROBE_EXT_ZFH) &&
+ !(pairs[0].value & RISCV_HWPROBE_EXT_ZFHMIN))
+ ksft_exit_fail_msg("Zfh reported without implied Zfhmin\n");
+ if ((pairs[0].value & RISCV_HWPROBE_EXT_ZVFH) &&
+ !(pairs[0].value & RISCV_HWPROBE_EXT_ZVFHMIN))
+ ksft_exit_fail_msg("Zvfh reported without implied Zvfhmin\n");
+ ksft_test_result_pass("Zfh/Zvfh imply Zfhmin/Zvfhmin\n");
+
ksft_finished();
}
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json b/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json
new file mode 100644
index 000000000000..ef7ca4a6775b
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/batch-delete.json
@@ -0,0 +1,115 @@
+[
+ {
+ "id": "d710",
+ "name": "Release tail references after first action deletion fails",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC actions add action pass index 2",
+ "$TC actions add action pass index 3",
+ "$TC filter add dev $DEV1 protocol ip ingress u32 match u32 0 0 action gact index 1"
+ ],
+ "cmdUnderTest": "$TC actions del action gact index 1 action gact index 2 action gact index 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions ls action gact",
+ "matchPattern": "total acts 3\\b.*index 1 ref 2 bind 1\\b.*index 2 ref 1 bind 0\\b.*index 3 ref 1 bind 0\\b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ]
+ ]
+ },
+ {
+ "id": "d711",
+ "name": "Release tail references after middle action deletion fails",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC actions add action pass index 2",
+ "$TC actions add action pass index 3",
+ "$TC filter add dev $DEV1 protocol ip ingress u32 match u32 0 0 action gact index 2"
+ ],
+ "cmdUnderTest": "$TC actions del action gact index 1 action gact index 2 action gact index 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions ls action gact",
+ "matchPattern": "total acts 2\\b.*index 2 ref 2 bind 1\\b.*index 3 ref 1 bind 0\\b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ]
+ ]
+ },
+ {
+ "id": "d713",
+ "name": "Delete a tail action once after a failed batch",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ],
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC actions add action pass index 2",
+ "$TC filter add dev $DEV1 protocol ip ingress u32 match u32 0 0 action gact index 1"
+ ],
+ "cmdUnderTest": "$TC actions del action gact index 1 action gact index 2",
+ "expExitCode": "255",
+ "verifyCmd": "sh -c '$TC actions del action gact index 2 && $TC actions ls action gact'",
+ "matchPattern": "total acts 1\\b.*index 1 ref 2 bind 1\\b",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress",
+ [
+ "$TC actions flush action gact",
+ 0,
+ 1,
+ 255
+ ]
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json
new file mode 100644
index 000000000000..e92a4180db68
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json
@@ -0,0 +1,78 @@
+[
+ {
+ "id": "e3b1",
+ "name": "Failed action batch releases a bound action reference",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1"
+ ],
+ "cmdUnderTest": "$TC actions replace action pass index 1 action goto chain 42 index 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions ls action gact",
+ "matchPattern": "total acts 1.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "e3b2",
+ "name": "Failed action batch releases entries after a bound action",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1",
+ [
+ "$TC actions replace action pass index 1 action pass index 2 action goto chain 42 index 3",
+ 255
+ ]
+ ],
+ "cmdUnderTest": "$TC actions add action pass index 2",
+ "expExitCode": "0",
+ "verifyCmd": "$TC actions ls action gact",
+ "matchPattern": "total acts 2.*index 1 ref 2 bind 1.*index 2 ref 1 bind 0",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "e3b3",
+ "name": "Failed action batch releases repeated references to a bound action",
+ "category": [
+ "actions",
+ "gact"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC actions add action pass index 1",
+ "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1"
+ ],
+ "cmdUnderTest": "$TC actions replace action pass index 1 action pass index 1 action goto chain 42 index 3",
+ "expExitCode": "255",
+ "verifyCmd": "$TC actions ls action gact",
+ "matchPattern": "total acts 1.*index 1 ref 2 bind 1",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json
index 05cedca67cca..2d5843aebd72 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json
@@ -202,5 +202,215 @@
"teardown": [
"$TC qdisc del dev $DEV1 parent root drr"
]
+ },
+ {
+ "id": "a7d2",
+ "name": "Delete a route filter that was moved to another bucket",
+ "category": [
+ "filter",
+ "route"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 1 to 1 classid 1:1",
+ "$TC filter change dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10001 route from 1 to 2 classid 1:1",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 200 route from 5 to 5 classid 1:5"
+ ],
+ "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10002 route from 1 to 2",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:",
+ "matchJSON": [
+ {
+ "protocol": "ip",
+ "pref": 200,
+ "kind": "route",
+ "chain": 0
+ },
+ {
+ "protocol": "ip",
+ "pref": 200,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x50005",
+ "flowid": "1:5"
+ }
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "c05a",
+ "name": "Try to change a route filter onto an already used handle",
+ "category": [
+ "filter",
+ "route"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 1 to 1 classid 1:1 action ok",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 2 to 2 classid 1:2 action drop"
+ ],
+ "cmdUnderTest": "$TC filter change dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10001 route from 2 to 2 classid 1:1 action ok",
+ "expExitCode": "2",
+ "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:",
+ "matchJSON": [
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x10001",
+ "flowid": "1:1",
+ "actions": [
+ {
+ "order": 1,
+ "kind": "gact",
+ "control_action": {
+ "type": "pass"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x20002",
+ "flowid": "1:2",
+ "actions": [
+ {
+ "order": 1,
+ "kind": "gact",
+ "control_action": {
+ "type": "drop"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "3f21",
+ "name": "Replace a route filter that shares its key with another filter",
+ "category": [
+ "filter",
+ "route"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:1",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:2"
+ ],
+ "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:9",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:",
+ "matchJSON": [
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x10101",
+ "flowid": "1:9"
+ }
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x10201",
+ "flowid": "1:2"
+ }
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
+ },
+ {
+ "id": "9d0e",
+ "name": "Replace both route filters sharing a key",
+ "category": [
+ "filter",
+ "route"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 ingress",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:1",
+ "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:2",
+ "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:9"
+ ],
+ "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:8",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:",
+ "matchJSON": [
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x10101",
+ "flowid": "1:9"
+ }
+ },
+ {
+ "protocol": "ip",
+ "pref": 100,
+ "kind": "route",
+ "chain": 0,
+ "options": {
+ "fh": "0x10201",
+ "flowid": "1:8"
+ }
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 ingress"
+ ]
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json
index 6d515d0e5ed6..a894e6f0e267 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json
@@ -213,5 +213,77 @@
"matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1p target 5ms interval 100ms",
"matchCount": "1",
"teardown": ["$TC qdisc del dev $DEV1 handle 1: root"]
+ },
+ {
+ "id": "6e44",
+ "name": "Create CODEL with 1us interval, accepted (sub-tick, uAPI locked)",
+ "category": [
+ "qdisc",
+ "codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel interval 1us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 0us",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "a8c3",
+ "name": "Create CODEL with 3us interval, accepted (two ticks)",
+ "category": [
+ "qdisc",
+ "codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel interval 3us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 2us",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "a695",
+ "name": "Create CODEL with 1024us interval boundary accepted",
+ "category": [
+ "qdisc",
+ "codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel interval 1024us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 1.02ms",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "9793",
+ "name": "Create CODEL with 1us target, accepted (target not in control law)",
+ "category": [
+ "qdisc",
+ "codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel target 1us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 0us interval 100ms",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json
index ee09e6d6fdf3..d2eab61c099a 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json
@@ -987,7 +987,7 @@
},
{
"id": "41f5",
- "name": "ETS offload where the sum of quanta wraps u32",
+ "name": "ETS offload with out-of-range quanta clamped",
"category": [
"qdisc",
"ets"
@@ -1002,7 +1002,7 @@
"cmdUnderTest": "$TC qdisc add dev $ETH root ets quanta 4294967294 1 1",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $ETH",
- "matchPattern": "qdisc ets .*bands 3 quanta 4294967294 1 1",
+ "matchPattern": "qdisc ets .*bands 3 quanta 1048576 256 256",
"matchCount": "1",
"teardown": [
"echo \"1\" > /sys/bus/netdevsim/del_device"
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json
index 4ce62b857fd7..de6a1b8d954a 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json
@@ -316,5 +316,77 @@
"matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 1p flows 1024 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64",
"matchCount": "1",
"teardown": ["$TC qdisc del dev $DEV1 handle 1: root"]
+ },
+ {
+ "id": "1b4d",
+ "name": "Create FQ_CODEL with 1us interval, accepted (sub-tick, uAPI locked)",
+ "category": [
+ "qdisc",
+ "fq_codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel interval 1us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum [0-9]+ target 5ms interval 0us memory_limit 32Mb ecn drop_batch 64",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "3540",
+ "name": "Create FQ_CODEL with 3us interval, accepted (two ticks)",
+ "category": [
+ "qdisc",
+ "fq_codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel interval 3us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum [0-9]+ target 5ms interval 2us memory_limit 32Mb ecn drop_batch 64",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "49c5",
+ "name": "Create FQ_CODEL with 1024us interval boundary accepted",
+ "category": [
+ "qdisc",
+ "fq_codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel interval 1024us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum [0-9]+ target 5ms interval 1.02ms memory_limit 32Mb ecn drop_batch 64",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
+ },
+ {
+ "id": "3e0f",
+ "name": "Create FQ_CODEL with 1us target, accepted (target not in control law)",
+ "category": [
+ "qdisc",
+ "fq_codel"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel target 1us",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum [0-9]+ target 0us interval 100ms memory_limit 32Mb ecn drop_batch 64",
+ "matchCount": "1",
+ "teardown": ["$TC qdisc del dev $DUMMY handle 1: root"]
}
]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf_flows_limit.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf_flows_limit.json
new file mode 100644
index 000000000000..44538b9266b6
--- /dev/null
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf_flows_limit.json
@@ -0,0 +1,128 @@
+[
+ {
+ "id": "e3cc",
+ "name": "HHF hh_limit rejects value above 2*HH_FLOWS_CNT cap (4294967295)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY handle 1: root hhf"
+ ],
+ "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root hhf hh_limit 4294967295",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ },
+ {
+ "id": "f681",
+ "name": "HHF hh_limit rejects 65536 (above 2*HH_FLOWS_CNT cap)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY handle 1: root hhf"
+ ],
+ "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root hhf hh_limit 65536",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ },
+ {
+ "id": "223d",
+ "name": "HHF hh_limit accepts boundary value 2048 (2*HH_FLOWS_CNT)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY handle 1: root hhf hh_limit 100"
+ ],
+ "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root hhf hh_limit 2048",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ },
+ {
+ "id": "147f",
+ "name": "HHF hh_limit rejects first value above cap (2049)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY handle 1: root hhf"
+ ],
+ "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root hhf hh_limit 2049",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ },
+ {
+ "id": "4d4f",
+ "name": "HHF add-time hh_limit 500 is preserved (init does not clobber user value)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf hh_limit 500",
+ "expExitCode": "0",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 500",
+ "matchCount": "1",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ },
+ {
+ "id": "ca99",
+ "name": "HHF add-time hh_limit 4294967295 is rejected (no qdisc installed)",
+ "category": [
+ "qdisc",
+ "hhf"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf hh_limit 4294967295",
+ "expExitCode": "2",
+ "verifyCmd": "$TC qdisc show dev $DUMMY",
+ "matchPattern": "qdisc hhf 1: root",
+ "matchCount": "0",
+ "teardown": []
+ }
+]
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
index 30da27fe8806..a6e25e76ecb1 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json
@@ -105,5 +105,209 @@
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
+ },
+ {
+ "id": "dbe3",
+ "name": "Set tx_queue_len to S16_MAX boundary (32767 accepted)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 32767",
+ "expExitCode": "0",
+ "verifyCmd": "$IP link show dev $DUMMY",
+ "matchPattern": "qlen 32767$",
+ "matchCount": "1",
+ "teardown": []
+ },
+ {
+ "id": "b50e",
+ "name": "Reject tx_queue_len above S16_MAX at set time (32768)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 32768",
+ "expExitCode": "2",
+ "verifyCmd": "$IP link show dev $DUMMY",
+ "matchPattern": "qlen 1000$",
+ "matchCount": "1",
+ "teardown": []
+ },
+ {
+ "id": "40f8",
+ "name": "Reject tx_queue_len above S16_MAX via sysfs (32768)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "sh -c 'echo 32768 > /sys/class/net/$DUMMY/tx_queue_len'",
+ "expExitCode": "1",
+ "verifyCmd": "$IP link show dev $DUMMY",
+ "matchPattern": "qlen 1000$",
+ "matchCount": "1",
+ "teardown": []
+ },
+ {
+ "id": "4b6e",
+ "name": "Set tx_queue_len to S16_MAX via sysfs (32767 accepted)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [],
+ "cmdUnderTest": "sh -c 'echo 32767 > /sys/class/net/$DUMMY/tx_queue_len'",
+ "expExitCode": "0",
+ "verifyCmd": "$IP link show dev $DUMMY",
+ "matchPattern": "qlen 32767$",
+ "matchCount": "1",
+ "teardown": []
+ },
+ {
+ "id": "b90d",
+ "name": "Create device with tx_queue_len at S16_MAX boundary (32767 accepted)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$IP link del dev $DUMMY",
+ 0,
+ 1
+ ]
+ ],
+ "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 32767 type dummy",
+ "expExitCode": "0",
+ "verifyCmd": "$IP link show dev $DUMMY",
+ "matchPattern": "qlen 32767$",
+ "matchCount": "1",
+ "teardown": [
+ [
+ "$IP link del dev $DUMMY",
+ 0,
+ 1
+ ]
+ ]
+ },
+ {
+ "id": "57ab",
+ "name": "Reject creating device with tx_queue_len above S16_MAX (32768)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$IP link del dev $DUMMY",
+ 0,
+ 1
+ ]
+ ],
+ "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 32768 type dummy",
+ "expExitCode": "2",
+ "verifyCmd": "$IP -o link show",
+ "matchPattern": "^[0-9]+: $DUMMY",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "e777",
+ "name": "Reject creating device with oversized tx_queue_len (500000)",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$IP link del dev $DUMMY",
+ 0,
+ 1
+ ]
+ ],
+ "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 500000 type dummy",
+ "expExitCode": "2",
+ "verifyCmd": "$IP -o link show",
+ "matchPattern": "^[0-9]+: $DUMMY",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "31ac",
+ "name": "Reject veth peer nest tx_queue_len above S16_MAX at create",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$IP link del dev $DEV1",
+ 0,
+ 1
+ ]
+ ],
+ "cmdUnderTest": "$IP link add dev $DEV1 type veth peer name $DEV0 txqueuelen 500000",
+ "expExitCode": "2",
+ "verifyCmd": "$IP -o link show",
+ "matchPattern": "^[0-9]+: $DEV1",
+ "matchCount": "0",
+ "teardown": []
+ },
+ {
+ "id": "b567",
+ "name": "Accept veth peer nest tx_queue_len within S16_MAX",
+ "category": [
+ "qdisc",
+ "pfifo_fast"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ [
+ "$IP link del dev $DEV1",
+ 0,
+ 1
+ ]
+ ],
+ "cmdUnderTest": "$IP link add dev $DEV1 txqueuelen 100 type veth peer name $DEV0 txqueuelen 200",
+ "expExitCode": "0",
+ "verifyCmd": "$IP link show",
+ "matchPattern": "qlen (100|200)$",
+ "matchCount": "2",
+ "teardown": [
+ [
+ "$IP link del dev $DEV0",
+ 0,
+ 1
+ ]
+ ]
}
]
diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile
index 5daf36c6c36c..37883e9d50ec 100644
--- a/tools/testing/selftests/ublk/Makefile
+++ b/tools/testing/selftests/ublk/Makefile
@@ -73,6 +73,8 @@ TEST_PROGS += test_stress_08.sh
TEST_PROGS += test_stress_09.sh
TEST_FILES := settings
+TEST_FILES += test_common.sh
+TEST_FILES += trace
TEST_GEN_PROGS_EXTENDED = kublk metadata_size
STANDALONE_UTILS := metadata_size.c
diff --git a/tools/testing/selftests/ublk/test_recover_03.sh b/tools/testing/selftests/ublk/test_recover_03.sh
index 2554805e5b02..92f4012178f0 100755
--- a/tools/testing/selftests/ublk/test_recover_03.sh
+++ b/tools/testing/selftests/ublk/test_recover_03.sh
@@ -29,6 +29,11 @@ _create_backfile 0 256M
_create_backfile 1 128M
_create_backfile 2 128M
+ublk_run_quiesce_recover -t null -q 2 -r 1 -b &
+ublk_run_quiesce_recover -t loop -q 2 -r 1 -b "${UBLK_BACKFILES[0]}" &
+ublk_run_quiesce_recover -t stripe -q 2 -r 1 -b "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
+wait
+
ublk_run_quiesce_recover -t null -q 2 -r 1 &
ublk_run_quiesce_recover -t loop -q 2 -r 1 "${UBLK_BACKFILES[0]}" &
ublk_run_quiesce_recover -t stripe -q 2 -r 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 434065215d12..d478b13cc8d5 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -13,7 +13,7 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh "$(CC)" trivial_program.c -no-pie)
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
check_initial_reg_state sigreturn iopl ioperm \
test_vsyscall mov_ss_trap sigtrap_loop \
- syscall_arg_fault fsgsbase_restore sigaltstack
+ syscall_arg_fault fsgsbase_restore sigaltstack int_signal
TARGETS_C_BOTHBITS += nx_stack
TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
diff --git a/tools/testing/selftests/x86/int_signal.c b/tools/testing/selftests/x86/int_signal.c
new file mode 100644
index 000000000000..22676dac72b5
--- /dev/null
+++ b/tools/testing/selftests/x86/int_signal.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Check the signal context for INT instructions with IDT and FRED entry. */
+#define _GNU_SOURCE
+
+#include <cpuid.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <ucontext.h>
+
+#include "helpers.h"
+
+#ifdef __x86_64__
+#define REG_IP REG_RIP
+#define USER_IP rip
+#define STACK_PTR "%rsp"
+#else
+#define REG_IP REG_EIP
+#define USER_IP eip
+#define STACK_PTR "%esp"
+#endif
+
+/*
+ * Each instruction has normal and single-step entry points. Resume at the
+ * NOP after handling its signal, then expect a trace trap after that NOP
+ * when TF is set. Explicit labels avoid assuming the kernel's saved IP.
+ */
+#define PROBE(name, insn) \
+ extern void name(void); \
+ extern void name##_tf(void); \
+ extern const char name##_end[], name##_step[]; \
+ asm(".pushsection .text\n" \
+ ".globl " #name "_tf\n" \
+ ".type " #name "_tf, @function\n" \
+ #name "_tf:\n" \
+ "pushf\n" \
+ "orl $0x100, (" STACK_PTR ")\n" \
+ "popf\n" \
+ ".globl " #name "\n" \
+ ".type " #name ", @function\n" \
+ #name ":\n" insn "\n" \
+ ".globl " #name "_end\n" \
+ #name "_end:\nnop\n" \
+ ".globl " #name "_step\n" \
+ #name "_step:\nret\n" \
+ ".size " #name ", .-" #name "\n" \
+ ".size " #name "_tf, .-" #name "_tf\n" \
+ ".popsection\n")
+
+PROBE(int1, ".byte 0xcd, 0x01");
+PROBE(int29, ".byte 0xcd, 0x29");
+PROBE(int2c, ".byte 0xcd, 0x2c");
+PROBE(int2d, ".byte 0xcd, 0x2d");
+PROBE(prefixed_int2d, ".byte 0x66, 0xcd, 0x2d");
+PROBE(long_int2d, ".fill 13, 1, 0x2e\n.byte 0xcd, 0x2d");
+PROBE(int81, ".byte 0xcd, 0x81");
+PROBE(intff, ".byte 0xcd, 0xff");
+PROBE(short_int3, ".byte 0xcc");
+PROBE(long_int3, ".byte 0xcd, 0x03");
+PROBE(int4, ".byte 0xcd, 0x04");
+PROBE(ud2, ".byte 0x0f, 0x0b");
+PROBE(hlt, ".byte 0xf4");
+
+struct test {
+ const char *name;
+ void (*run)(void);
+ void (*run_tf)(void);
+ const char *end, *step;
+ int signo, trap, error, ip_offset, flags, code;
+};
+
+#define TEST(name, sig, trap, error, offset, flags, code) \
+ { #name, name, name##_tf, name##_end, name##_step, \
+ sig, trap, error, offset, flags, code }
+
+#define GP(name, error) \
+ TEST(name, SIGSEGV, 13, error, 0, X86_EFLAGS_RF, SI_KERNEL)
+
+static const struct test tests[] = {
+ GP(int1, 0x00a),
+ GP(int29, 0x14a),
+ GP(int2c, 0x162),
+ GP(int2d, 0x16a),
+ GP(prefixed_int2d, 0x16a),
+ GP(long_int2d, 0x16a),
+ GP(int81, 0x40a),
+ GP(intff, 0x7fa),
+ GP(hlt, 0),
+ TEST(short_int3, SIGTRAP, 3, 0, 1, 0, SI_KERNEL),
+ TEST(long_int3, SIGTRAP, 3, 0, 2, 0, SI_KERNEL),
+ TEST(int4, SIGSEGV, 4, 0, 2, 0, SI_KERNEL),
+ TEST(ud2, SIGILL, 6, 0, 0, X86_EFLAGS_RF, ILL_ILLOPN),
+};
+
+static const struct test *active;
+static volatile sig_atomic_t seen, signo, trap, error, ip_offset, flags;
+static volatile sig_atomic_t code, addr_ok, single_step, stepped, step_ok;
+
+static void handler(int sig, siginfo_t *info, void *context)
+{
+ ucontext_t *uc = context;
+ uintptr_t ip = uc->uc_mcontext.gregs[REG_IP];
+ uintptr_t start = (uintptr_t)active->run;
+ uintptr_t end = (uintptr_t)active->end;
+
+ if (seen && single_step && sig == SIGTRAP) {
+ if (stepped++) {
+ ksft_print_msg("%s: second trace trap at %#lx\n",
+ active->name, (unsigned long)ip);
+ _exit(KSFT_FAIL);
+ }
+ step_ok = ip == (uintptr_t)active->step &&
+ uc->uc_mcontext.gregs[REG_TRAPNO] == 1 &&
+ info->si_code == TRAP_TRACE;
+ uc->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
+ return;
+ }
+
+ if (seen || ip < start || ip > end) {
+ ksft_print_msg("%s: unexpected signal %d at %#lx\n",
+ active->name, sig, (unsigned long)ip);
+ _exit(KSFT_FAIL);
+ }
+
+ signo = sig;
+ trap = uc->uc_mcontext.gregs[REG_TRAPNO];
+ error = uc->uc_mcontext.gregs[REG_ERR];
+ ip_offset = ip - start;
+ flags = uc->uc_mcontext.gregs[REG_EFL] & (X86_EFLAGS_RF | X86_EFLAGS_TF);
+ code = info->si_code;
+ /* force_sig() reports no address, force_sig_fault() reports the IP. */
+ addr_ok = info->si_addr == (code == SI_KERNEL ? NULL : (void *)ip);
+ seen = 1;
+ uc->uc_mcontext.gregs[REG_IP] = end;
+}
+
+static void wait_for_child(pid_t child, int *status)
+{
+ pid_t ret;
+
+ do {
+ ret = waitpid(child, status, 0);
+ } while (ret < 0 && errno == EINTR);
+ if (ret != child)
+ ksft_exit_fail_perror("waitpid");
+}
+
+/* Resume the tracee and check where the next stop lands. */
+static bool resume_to(pid_t child, int *status, int request, int sig,
+ const void *ip, const char *what)
+{
+ struct user_regs_struct regs;
+
+ if (ptrace(request, child, 0, 0))
+ return false;
+ wait_for_child(child, status);
+ if (!WIFSTOPPED(*status)) {
+ ksft_print_msg("%s: tracee did not stop\n", what);
+ return false;
+ }
+ if (WSTOPSIG(*status) != sig) {
+ ksft_print_msg("%s: stopped with signal %d, expected %d\n",
+ what, WSTOPSIG(*status), sig);
+ return false;
+ }
+ if (ptrace(PTRACE_GETREGS, child, 0, &regs))
+ return false;
+ if ((unsigned long)regs.USER_IP != (unsigned long)ip) {
+ ksft_print_msg("%s: stopped at %#lx, expected %#lx\n", what,
+ (unsigned long)regs.USER_IP, (unsigned long)ip);
+ return false;
+ }
+ return true;
+}
+
+static bool set_ip(pid_t child, const void *ip, bool tf)
+{
+ struct user_regs_struct regs;
+
+ if (ptrace(PTRACE_GETREGS, child, 0, &regs))
+ return false;
+ regs.USER_IP = (unsigned long)ip;
+ if (tf)
+ regs.eflags |= X86_EFLAGS_TF;
+ return !ptrace(PTRACE_SETREGS, child, 0, &regs);
+}
+
+/*
+ * Exercise the tracer paths that resume through the fault frame rather than
+ * sigreturn. A stale FRED software event flag on that frame traps before the
+ * NOP executes instead of after it.
+ */
+static void test_ptrace(void)
+{
+ bool into = false, step = false, cont = false;
+ pid_t child;
+ int status;
+
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_perror("fork");
+ if (!child) {
+ if (ptrace(PTRACE_TRACEME, 0, 0, 0))
+ _exit(KSFT_FAIL);
+ /* Start from a breakpoint frame, not the syscall frame of raise(). */
+ asm volatile("int3");
+ _exit(KSFT_FAIL);
+ }
+
+ wait_for_child(child, &status);
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
+ goto out;
+ if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_EXITKILL))
+ goto out;
+
+ /* Single-step into the INT. The fault must report the INT's address. */
+ if (!set_ip(child, int2d, false))
+ goto out;
+ into = resume_to(child, &status, PTRACE_SINGLESTEP, SIGSEGV, int2d,
+ "single-step into INT");
+ if (!into)
+ goto out;
+
+ /* Suppress SIGSEGV and single-step the NOP. */
+ if (!set_ip(child, int2d_end, false))
+ goto out;
+ step = resume_to(child, &status, PTRACE_SINGLESTEP, SIGTRAP, int2d_step,
+ "single-step after INT");
+ if (!step)
+ goto out;
+
+ /* Fault again, then suppress SIGSEGV and continue with TF set. */
+ if (!set_ip(child, int2d, false))
+ goto out;
+ if (!resume_to(child, &status, PTRACE_CONT, SIGSEGV, int2d,
+ "continue to INT"))
+ goto out;
+ if (!set_ip(child, int2d_end, true))
+ goto out;
+ cont = resume_to(child, &status, PTRACE_CONT, SIGTRAP, int2d_step,
+ "continue with TF after INT");
+out:
+ if (WIFSTOPPED(status)) {
+ kill(child, SIGKILL);
+ wait_for_child(child, &status);
+ }
+ ksft_test_result(into, "ptrace single-step into INT faults at the INT\n");
+ ksft_test_result(step, "ptrace single-step after suppressing SIGSEGV\n");
+ ksft_test_result(cont, "ptrace continue with TF after suppressing SIGSEGV\n");
+}
+
+static bool cpu_has_fred(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ if (__get_cpuid_max(0, NULL) < 7)
+ return false;
+ __cpuid_count(7, 1, eax, ebx, ecx, edx);
+ return eax & (1 << 17);
+}
+
+int main(void)
+{
+ unsigned int i, tf;
+ int expected_flags, ok;
+
+ ksft_print_header();
+ ksft_set_plan(2 * ARRAY_SIZE(tests) + 3);
+ ksft_print_msg("CPU %s FRED\n", cpu_has_fred() ? "supports" : "lacks");
+ sethandler(SIGSEGV, handler, 0);
+ sethandler(SIGTRAP, handler, 0);
+ sethandler(SIGILL, handler, 0);
+
+ for (tf = 0; tf < 2; tf++) {
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ active = &tests[i];
+ single_step = tf;
+ seen = signo = trap = error = ip_offset = flags = 0;
+ code = addr_ok = stepped = step_ok = 0;
+ expected_flags = active->flags | (tf ? X86_EFLAGS_TF : 0);
+ if (tf)
+ active->run_tf();
+ else
+ active->run();
+
+ ok = seen && signo == active->signo && trap == active->trap &&
+ error == active->error && ip_offset == active->ip_offset &&
+ flags == expected_flags && code == active->code && addr_ok &&
+ (!tf || (stepped && step_ok));
+ ksft_test_result(ok, "%s%s\n", active->name, tf ? " with TF" : "");
+ if (!ok) {
+ ksft_print_msg("got signal=%d trap=%d error=%#x ip=%d\n",
+ signo, trap, error, ip_offset);
+ ksft_print_msg("got flags=%#x code=%d addr_ok=%d step_ok=%d\n",
+ flags, code, addr_ok, step_ok);
+ ksft_print_msg("expected signal=%d trap=%d error=%#x ip=%d\n",
+ active->signo, active->trap, active->error,
+ active->ip_offset);
+ ksft_print_msg("expected flags=%#x code=%d\n",
+ expected_flags, active->code);
+ }
+ }
+ }
+ test_ptrace();
+ ksft_finished();
+}