Package: release.debian.org Severity: normal Tags: bookworm X-Debbugs-Cc: [email protected] Control: affects -1 + src:openvswitch User: [email protected] Usertags: pu
Hi, I'd like to update openvswitch to fix CVE-2026-34956. [ Tests ] There's included unit tests, including new ones that are actually testing the bug. [ Checklist ] [x] *all* changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in (old)stable [x] the issue is verified as fixed in unstable Note that the changelog in attached debdiff targets trixie-security, though I'll obviously fix this before rebuild and upload, so please ignore it. Cheers, Thomas Goirand (zigo)
diff -Nru openvswitch-3.1.0/debian/changelog openvswitch-3.1.0/debian/changelog --- openvswitch-3.1.0/debian/changelog 2024-02-18 16:46:26.000000000 +0100 +++ openvswitch-3.1.0/debian/changelog 2026-04-01 09:34:09.000000000 +0200 @@ -1,3 +1,11 @@ +openvswitch (3.1.0-2+deb12u2) bookworm-security; urgency=medium + + * CVE-2026-34956: Invalid memory access in conntrack FTP alg. Applied + upstream patch: conntrack: Fix replace_substring to handle larger packets. + (Closes: #1132449). + + -- Thomas Goirand <[email protected]> Wed, 01 Apr 2026 09:34:09 +0200 + openvswitch (3.1.0-2+deb12u1) bookworm-security; urgency=medium * CVE-2023-5366: A flaw was found in Open vSwitch that allows ICMPv6 Neighbor diff -Nru openvswitch-3.1.0/debian/patches/CVE-2026-34956_conntrack_fix_replace_substring_to_handle_larger_packets.patch openvswitch-3.1.0/debian/patches/CVE-2026-34956_conntrack_fix_replace_substring_to_handle_larger_packets.patch --- openvswitch-3.1.0/debian/patches/CVE-2026-34956_conntrack_fix_replace_substring_to_handle_larger_packets.patch 1970-01-01 01:00:00.000000000 +0100 +++ openvswitch-3.1.0/debian/patches/CVE-2026-34956_conntrack_fix_replace_substring_to_handle_larger_packets.patch 2026-04-01 09:34:09.000000000 +0200 @@ -0,0 +1,282 @@ +Author: Aaron Conole <[email protected]> +Date: Tue, 31 Mar 2026 14:39:23 +0200 +Description: conntrack: Fix replace_substring to handle larger packets. + There is a buffer size calculation issue in replace_string that can + result in a heap overflow with a specially crafted FTP packet. This + is a result of integer truncation when downscaling from size_t into + uint8_t size. Correct this by setting the types to size_t until the + underlying memmove to keep the sizes intact. + . + The total_size, substr_size, and rep_str_size are expected to all be + sane values for the memmove, and modify_packet also expects this, so + document that as well. In the case of FTP, those are enforced in + repl_ftp_v*_addr at the checks for MAX_FTP_V*_NAT_DELTA, and the + packet data itself should be sanitized by the ovs_strlcpy that runs + early to extract a string of appropriate length. + . +Fixes: bd5e81a0e596 ("Userspace Datapath: Add ALG infra and FTP.") +Reported-by: Seiji Sakurai <[email protected]> +Signed-off-by: Aaron Conole <[email protected]> +Signed-off-by: Ilya Maximets <[email protected]> +Origin: upstream, https://github.com/openvswitch/ovs/commit/8e81545d9d1461758c72fbd08ce1f52e472bca94.patch +Bug-Debian: https://bugs.debian.org/1132449 +Last-Update: 2026-04-01 + +Index: openvswitch/AUTHORS.rst +=================================================================== +--- openvswitch.orig/AUTHORS.rst ++++ openvswitch/AUTHORS.rst +@@ -698,6 +698,7 @@ Scott Hendricks + Sean Brady [email protected] + Sebastian Andrzej Siewior [email protected] + Sébastien RICCIO [email protected] ++Seiji Sakurai [email protected] + Shweta Seth [email protected] + Simon Jouet [email protected] + Spiro Kourtessis [email protected] +Index: openvswitch/lib/conntrack.c +=================================================================== +--- openvswitch.orig/lib/conntrack.c ++++ openvswitch/lib/conntrack.c +@@ -2898,9 +2898,9 @@ expectation_create(struct conntrack *ct, + } + + static void +-replace_substring(char *substr, uint8_t substr_size, +- uint8_t total_size, char *rep_str, +- uint8_t rep_str_size) ++replace_substring(char *substr, size_t substr_size, ++ size_t total_size, char *rep_str, ++ size_t rep_str_size) + { + memmove(substr + rep_str_size, substr + substr_size, + total_size - substr_size); +@@ -2918,6 +2918,9 @@ repl_bytes(char *str, char c1, char c2) + } + } + ++/* Replaces a substring in the packet and rewrites the packet ++ * size to match. This function assumes the caller has verified ++ * the lengths to prevent under/over flow. */ + static void + modify_packet(struct dp_packet *pkt, char *pkt_str, size_t size, + char *repl_str, size_t repl_size, +Index: openvswitch/tests/library.at +=================================================================== +--- openvswitch.orig/tests/library.at ++++ openvswitch/tests/library.at +@@ -283,3 +283,7 @@ AT_CLEANUP + AT_SETUP([uuidset module]) + AT_CHECK([ovstest test-uuidset], [0], [], [ignore]) + AT_CLEANUP ++ ++AT_SETUP([Conntrack Library - FTP ALG parsing]) ++AT_CHECK([ovstest test-conntrack ftp-alg-large-payload]) ++AT_CLEANUP +Index: openvswitch/tests/test-conntrack.c +=================================================================== +--- openvswitch.orig/tests/test-conntrack.c ++++ openvswitch/tests/test-conntrack.c +@@ -29,6 +29,100 @@ + static const char payload[] = "50540000000a50540000000908004500001c0000000000" + "11a4cd0a0101010a0101020001000200080000"; + ++/* Build an Ethernet + IPv4 packet. If 'pkt' is NULL a new buffer is ++ * allocated with 64 bytes of extra headroom so the FTP MTU guard passes. ++ * The buffer is populated up through the IP header; l4 is set to point ++ * directly after the IP header. The caller is responsible for filling ++ * the L4 header and payload that follow. */ ++static struct dp_packet * ++build_eth_ip_packet(struct dp_packet *pkt, struct eth_addr eth_src, ++ struct eth_addr eth_dst, ovs_be32 ip_src, ovs_be32 ip_dst, ++ uint8_t proto, uint16_t payload_alloc) ++{ ++ struct ip_header *iph; ++ uint16_t proto_len; ++ ++ switch (proto) { ++ case IPPROTO_TCP: proto_len = TCP_HEADER_LEN; break; ++ case IPPROTO_UDP: proto_len = UDP_HEADER_LEN; break; ++ case IPPROTO_ICMP: proto_len = ICMP_HEADER_LEN; break; ++ default: proto_len = 0; break; ++ } ++ ++ if (pkt == NULL) { ++ /* 64-byte extra headroom keeps dp_packet_get_allocated() large enough ++ * that the FTP V4 MTU guard (orig_used_size + 8 <= allocated) passes ++ * even when the packet is near its maximum size. */ ++ pkt = dp_packet_new_with_headroom(ETH_HEADER_LEN + IP_HEADER_LEN ++ + proto_len + payload_alloc, 64); ++ } ++ ++ eth_compose(pkt, eth_src, eth_dst, ETH_TYPE_IP, ++ IP_HEADER_LEN + proto_len + payload_alloc); ++ iph = dp_packet_l3(pkt); ++ iph->ip_ihl_ver = IP_IHL_VER(5, 4); ++ iph->ip_tot_len = htons(IP_HEADER_LEN + proto_len + payload_alloc); ++ iph->ip_ttl = 64; ++ iph->ip_proto = proto; ++ packet_set_ipv4_addr(pkt, &iph->ip_src, ip_src); ++ packet_set_ipv4_addr(pkt, &iph->ip_dst, ip_dst); ++ iph->ip_csum = csum(iph, IP_HEADER_LEN); ++ dp_packet_set_l4(pkt, (char *) iph + IP_HEADER_LEN); ++ return pkt; ++} ++ ++/* Fill the TCP header and optional payload for a packet previously built with ++ * build_eth_ip_packet(). The 'payload' buffer of 'payload_len' bytes is ++ * appended after the TCP header if non-NULL. IP total-length, IP checksum, ++ * and TCP checksum are all updated to reflect the final packet contents. */ ++static struct dp_packet * ++build_tcp_packet(struct dp_packet *pkt, uint16_t tcp_src, uint16_t tcp_dst, ++ uint16_t tcp_flags, const char *tcp_payload, ++ size_t payload_len) ++{ ++ struct tcp_header *tcph; ++ struct ip_header *iph; ++ uint16_t ip_tot_len; ++ uint32_t tcp_csum; ++ struct flow flow; ++ ++ ovs_assert(pkt); ++ tcph = dp_packet_l4(pkt); ++ ovs_assert(tcph); ++ ++ tcph->tcp_src = htons(tcp_src); ++ tcph->tcp_dst = htons(tcp_dst); ++ put_16aligned_be32(&tcph->tcp_seq, 0); ++ put_16aligned_be32(&tcph->tcp_ack, 0); ++ tcph->tcp_ctl = TCP_CTL(tcp_flags, TCP_HEADER_LEN / 4); ++ tcph->tcp_winsz = htons(65535); ++ tcph->tcp_csum = 0; ++ tcph->tcp_urg = 0; ++ ++ if (tcp_payload && payload_len > 0) { ++ /* The caller must have pre-allocated space via build_eth_ip_packet's ++ * payload_alloc argument. Write directly to avoid a realloc that ++ * would lose the extra headroom required by the FTP MTU guard. */ ++ memcpy((char *) tcph + TCP_HEADER_LEN, tcp_payload, payload_len); ++ } ++ ++ /* Update IP total length and recompute IP checksum. */ ++ iph = dp_packet_l3(pkt); ++ ip_tot_len = IP_HEADER_LEN + TCP_HEADER_LEN + payload_len; ++ iph->ip_tot_len = htons(ip_tot_len); ++ iph->ip_csum = 0; ++ iph->ip_csum = csum(iph, IP_HEADER_LEN); ++ ++ /* Compute TCP checksum over pseudo-header + TCP segment. */ ++ tcp_csum = packet_csum_pseudoheader(iph); ++ tcph->tcp_csum = csum_finish( ++ csum_continue(tcp_csum, tcph, TCP_HEADER_LEN + payload_len)); ++ ++ /* Set l3/l4 offsets so conntrack can extract a flow key. */ ++ flow_extract(pkt, &flow); ++ return pkt; ++} ++ + static struct dp_packet_batch * + prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type) + { +@@ -252,6 +346,87 @@ test_pcap(struct ovs_cmdl_context *ctx) + ovs_pcap_close(pcap); + } + ++/* ALG related testing. */ ++ ++/* FTP IPv4 PORT payload for testing. */ ++#define FTP_PORT_CMD_STR "PORT 192,168,123,2,113,42\r\n" ++#define FTP_CMD_PAD 234 ++#define FTP_PAYLOAD_LEN (sizeof FTP_PORT_CMD_STR - 1 + FTP_CMD_PAD) ++ ++/* Test modify_packet wrapping. ++ * ++ * The test builds a minimal FTP control-channel exchange: ++ * 1. A TCP SYN that creates a conntrack entry with helper=ftp and SNAT. ++ * 2. A PSH|ACK carrying "PORT 192,168,123,2,113,42\r\n" padded to exactly ++ * 261 bytes of TCP payload, which makes total_size == 256. ++ * ++ * After the PORT packet is processed the address field in the payload must ++ * read "192,168,1,1" (the SNAT address with dots replaced by commas). */ ++static void ++test_ftp_alg_large_payload(struct ovs_cmdl_context *ctx OVS_UNUSED) ++{ ++ /* Packet endpoints. */ ++ struct eth_addr eth_src = ETH_ADDR_C(00, 01, 02, 03, 04, 05); ++ struct eth_addr eth_dst = ETH_ADDR_C(00, 06, 07, 08, 09, 0a); ++ ovs_be32 ip_src = inet_addr("192.168.123.2"); /* FTP client. */ ++ ovs_be32 ip_dst = inet_addr("192.168.1.1"); /* FTP server / SNAT addr. */ ++ uint16_t sport = 12345; ++ uint16_t dport = 21; /* FTP control port. */ ++ ++ /* SNAT: rewrite client address to 192.168.1.1 in PORT commands. */ ++ struct nat_action_info_t nat_info; ++ memset(&nat_info, 0, sizeof nat_info); ++ nat_info.nat_action = NAT_ACTION_SRC; ++ nat_info.min_addr.ipv4 = ip_dst; ++ nat_info.max_addr.ipv4 = ip_dst; ++ ++ ct = conntrack_init(); ++ conntrack_set_tcp_seq_chk(ct, false); ++ ++ long long now = time_msec(); ++ ++ struct dp_packet *syn = build_eth_ip_packet(NULL, eth_src, eth_dst, ++ ip_src, ip_dst, ++ IPPROTO_TCP, 0); ++ build_tcp_packet(syn, sport, dport, TCP_SYN, NULL, 0); ++ ++ struct dp_packet_batch syn_batch; ++ dp_packet_batch_init_packet(&syn_batch, syn); ++ conntrack_execute(ct, &syn_batch, htons(ETH_TYPE_IP), false, true, 0, ++ 0, 0, NULL, NULL, "ftp", &nat_info, now, 0); ++ dp_packet_delete_batch(&syn_batch, true); ++ ++ /* We get to skip some of the processing because the conntrack execute ++ * above will create the required conntrack entries. */ ++ ++ /* Build the large payload: PORT command followed by padding spaces ++ * and a final "\r\n" to reach exactly FTP_PAYLOAD_LEN bytes. The ++ * FTP parser only looks at the first LARGEST_FTP_MSG_OF_INTEREST (128) ++ * bytes, so the trailing spaces do not interfere with parsing. */ ++ char ftp_payload[FTP_PAYLOAD_LEN]; ++ memcpy(ftp_payload, FTP_PORT_CMD_STR, sizeof FTP_PORT_CMD_STR - 1); ++ memset(ftp_payload + sizeof FTP_PORT_CMD_STR - 1, ' ', FTP_CMD_PAD); ++ ++ struct dp_packet *port_pkt = ++ build_eth_ip_packet(NULL, eth_src, eth_dst, ip_src, ip_dst, ++ IPPROTO_TCP, FTP_PAYLOAD_LEN); ++ build_tcp_packet(port_pkt, sport, dport, TCP_PSH | TCP_ACK, ++ ftp_payload, FTP_PAYLOAD_LEN); ++ ++ struct dp_packet_batch port_batch; ++ dp_packet_batch_init_packet(&port_batch, port_pkt); ++ conntrack_execute(ct, &port_batch, htons(ETH_TYPE_IP), false, true, 0, ++ 0, 0, NULL, NULL, "ftp", &nat_info, now, 0); ++ ++ struct tcp_header *th = dp_packet_l4(port_pkt); ++ size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4; ++ const char *ftp_start = (const char *) th + tcp_hdr_len; ++ ovs_assert(!strncmp(ftp_start, "PORT 192,168,1,1,", 17)); ++ dp_packet_delete_batch(&port_batch, true); ++ conntrack_destroy(ct); ++} ++ ++ + static const struct ovs_cmdl_command commands[] = { + /* Connection tracker tests. */ + /* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to +@@ -264,6 +439,13 @@ static const struct ovs_cmdl_command com + * 'batch_size' (1 by default) per call, with the commit flag set. + * Prints the ct_state of each packet. */ + {"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO}, ++ /* Verifies that the FTP ALG replace_substring function correctly handles ++ * a packet whose payload puts total_size at exactly 256 bytes. The ++ * original uint8_t parameter type truncated 256 to 0, leading to a ++ * near-SIZE_MAX memmove (heap overflow). The test confirms the address ++ * is rewritten to the SNAT target rather than causing a crash. */ ++ {"ftp-alg-large-payload", "", 0, 0, ++ test_ftp_alg_large_payload, OVS_RO}, + + {NULL, NULL, 0, 0, NULL, OVS_RO}, + }; diff -Nru openvswitch-3.1.0/debian/patches/series openvswitch-3.1.0/debian/patches/series --- openvswitch-3.1.0/debian/patches/series 2024-02-18 16:46:26.000000000 +0100 +++ openvswitch-3.1.0/debian/patches/series 2026-04-01 09:34:09.000000000 +0200 @@ -2,3 +2,4 @@ CVE-2023-1668_ofproto-dpif-xlate_Always_mask_ip_proto_field.patch CVE-2023-5366-Fix-missing-masks-on-a-final-stage-with-ports-trie.patch CVE-2023-3966-netdev-offload-tc_Check_geneve_metadata_length.patch +CVE-2026-34956_conntrack_fix_replace_substring_to_handle_larger_packets.patch

