Skip to content

[AutoPR- Security] Patch openvswitch for CVE-2026-34956 [MEDIUM]#17252

Open
azurelinux-security wants to merge 1 commit into
microsoft:3.0-devfrom
azurelinux-security:azure-autosec/openvswitch/3.0/1118727
Open

[AutoPR- Security] Patch openvswitch for CVE-2026-34956 [MEDIUM]#17252
azurelinux-security wants to merge 1 commit into
microsoft:3.0-devfrom
azurelinux-security:azure-autosec/openvswitch/3.0/1118727

Conversation

@azurelinux-security
Copy link
Copy Markdown
Contributor

@azurelinux-security azurelinux-security commented May 15, 2026

Auto Patch openvswitch for CVE-2026-34956.

Autosec pipeline run -> https://dev.azure.com/mariner-org/mariner/_build/results?buildId=1118727&view=results

Merge Checklist

All boxes should be checked before merging the PR (just tick any boxes which don't apply to this PR)

  • The toolchain has been rebuilt successfully (or no changes were made to it)
  • The toolchain/worker package manifests are up-to-date
  • Any updated packages successfully build (or no packages were changed)
  • Packages depending on static components modified in this PR (Golang, *-static subpackages, etc.) have had their Release tag incremented.
  • Package tests (%check section) have been verified with RUN_CHECK=y for existing SPEC files, or added to new SPEC files
  • All package sources are available
  • cgmanifest files are up-to-date and sorted (./cgmanifest.json, ./toolkit/scripts/toolchain/cgmanifest.json, .github/workflows/cgmanifest.json)
  • LICENSE-MAP files are up-to-date (./LICENSES-AND-NOTICES/SPECS/data/licenses.json, ./LICENSES-AND-NOTICES/SPECS/LICENSES-MAP.md, ./LICENSES-AND-NOTICES/SPECS/LICENSE-EXCEPTIONS.PHOTON)
  • All source files have up-to-date hashes in the *.signatures.json files
  • sudo make go-tidy-all and sudo make go-test-coverage pass
  • Documentation has been updated to match any changes to the build system
  • Ready to merge

Summary

What does the PR accomplish, why was it needed?

Change Log
Does this affect the toolchain?

YES/NO

Associated issues
  • N/A
Links to CVEs
Test Methodology

@microsoft-github-policy-service microsoft-github-policy-service Bot added Packaging 3.0-dev PRs Destined for AzureLinux 3.0 labels May 15, 2026
@Kanishk-Bansal Kanishk-Bansal marked this pull request as ready for review May 15, 2026 07:40
@Kanishk-Bansal Kanishk-Bansal requested a review from a team as a code owner May 15, 2026 07:40
@azurelinux-security
Copy link
Copy Markdown
Contributor Author

🔒 CVE Patch Review: CVE-2026-34956

PR #17252 — [AutoPR- Security] Patch openvswitch for CVE-2026-34956 [MEDIUM]
Package: openvswitch | Branch: 3.0-dev


Spec File Validation

Check Status Detail
Release bump Release bumped 2 → 3
Patch entry Patch entries added: ['CVE-2026-34956.patch'] (covers ['CVE-2026-34956'])
Patch application %autosetup/%autopatch found in full spec — patches applied automatically
Changelog Changelog entry looks good
Signatures No source tarball changes — signatures N/A
Manifests Not a toolchain PR — manifests N/A

Build Verification

  • Build status: ✅ PASSED
  • Artifact downloaded:
  • CVE applied during build:
  • Warnings (51):
    • L1316: time="2026-05-15T07:50:13Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1340: time="2026-05-15T07:50:13Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1747: time="2026-05-15T07:50:24Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1749: time="2026-05-15T07:50:24Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L2983: time="2026-05-15T07:50:46Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L2988: time="2026-05-15T07:50:47Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3404: time="2026-05-15T07:50:59Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3406: time="2026-05-15T07:50:59Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3512: time="2026-05-15T07:51:05Z" level=debug msg="libtool: warning: relinking 'ofproto/libofproto.la'"
    • L3530: time="2026-05-15T07:51:06Z" level=debug msg="libtool: warning: remember to run 'libtool --finish /usr/lib/openvswitch-dpdk'"
    • … and 41 more

🤖 AI Build Log Analysis

  • Risk: low
  • Summary: The openvswitch 3.3.0 package rebuilt successfully and produced all expected RPMs. The CVE-2026-34956 patch was applied during the %prep phase with no visible hunk failures or rejects, and the subsequent configure, compile, link, and packaging steps completed without errors. Tests were disabled (--nocheck), and the build emitted minor environment-related warnings, but none impacted the final artifacts.
  • AI-detected warnings:
    • rpmbuild emitted 'warning: Could not canonicalize hostname: e50f6ffec000000' during build.
    • Early chroot/setup scripts logged 'groupadd: command not found' but the build proceeded and later group creation succeeded.
    • An unrelated systemtap helper step reported '/usr/src/azl/BUILD/systemtap-5.0/missing: No such file or directory' followed by 'make: *** Error 127'; this did not affect the openvswitch build.
    • The test suite was skipped (--nocheck), so the CVE fix was not validated by unit or integration tests in this build.

🧪 Test Log Analysis

  • Test status: ❌ FAILED
  • Test errors (7):
    • L6476: time="2026-05-15T07:53:00Z" level=debug msg="2394: track, simple idl, initially populated, weak references, insert+delete batch - write-changed-only - C FAILED (ovsdb-idl.at:1469)"
    • L6490: time="2026-05-15T07:53:01Z" level=debug msg="2412: track, simple idl, initially empty, strong references, insert and delete - C FAILED (ovsdb-idl.at:1676)"
    • L6526: time="2026-05-15T07:53:02Z" level=debug msg="2445: track, insert and delete, refs to link1 - write-changed-only - C FAILED (ovsdb-idl.at:2615)"
    • L6706: time="2026-05-15T07:53:21Z" level=debug msg="ERROR: 2609 tests were run,"
    • L9205: time="2026-05-15T07:55:10Z" level=debug msg="2393: track, simple idl, initially populated, weak references, insert+delete batch - C FAILED (ovsdb-idl.at:1469)"
    • L9252: time="2026-05-15T07:55:11Z" level=debug msg="2446: track, insert and delete, refs to link2 - C FAILED (ovsdb-idl.at:2658)"
    • L9434: time="2026-05-15T07:55:31Z" level=debug msg="ERROR: 2609 tests were run,"
  • Test warnings (105):
    • L1324: time="2026-05-15T07:50:32Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1326: time="2026-05-15T07:50:32Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1724: time="2026-05-15T07:50:33Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L1726: time="2026-05-15T07:50:33Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L2990: time="2026-05-15T07:51:02Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L2992: time="2026-05-15T07:51:02Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3387: time="2026-05-15T07:51:03Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3389: time="2026-05-15T07:51:03Z" level=debug msg="/usr/src/azl/BUILD/openvswitch-3.3.0/build-dpdk/../ovsdb/ovsdb-doc:240: SyntaxWarning: invalid escape sequence '\\{'"
    • L3502: time="2026-05-15T07:51:13Z" level=debug msg="libtool: warning: relinking 'ofproto/libofproto.la'"
    • L3520: time="2026-05-15T07:51:13Z" level=debug msg="libtool: warning: remember to run 'libtool --finish /usr/lib/openvswitch-dpdk'"
🤖 AI Test Log Analysis
  • Risk: low
  • Summary: Open vSwitch built successfully with the CVE-2026-34956 patch applied. The test suite initially reported 2 unexpected failures out of 2609 tests with 4 skipped, but both failing cases passed on an immediate recheck and the overall check finished with exit status 0. No failures appear related to the CVE fix, and packaging completed without further issues.
  • AI-detected test issues:
    • Initial run: test 2393 ('track, simple idl, initially populated, weak references, insert+delete batch - C') failed unexpectedly; passed on recheck.
    • Initial run: test 2446 ('track, insert and delete, refs to link2 - C') failed unexpectedly; passed on recheck.

Patch Analysis

  • Match type: minor_differences
  • Risk assessment: low
  • Summary: The PR patch faithfully applies the upstream fix to change replace_substring() parameters from uint8_t to size_t and adds the same comment documenting the expectations in modify_packet(). It also includes the upstream test additions that exercise the FTP ALG corner case. The only missing upstream hunk is a non-functional AUTHORS.rst credit line, so the functional parts of the patch match upstream.
  • Missing hunks:
Detailed analysis

Core fix equivalence:

  • lib/conntrack.c: The PR changes replace_substring() signature exactly as upstream: parameters substr_size, total_size, and rep_str_size are converted from uint8_t to size_t, preventing truncation when values reach or exceed 256. The internal memmove invocation remains identical. The PR also adds the same clarifying comment above modify_packet() about caller-verified lengths. Context line numbers differ (2993 vs 3025) due to base differences, but the code hunk content matches.

Tests:

  • tests/library.at: The PR adds the same AT_SETUP/AT_CHECK stanza to run the new FTP ALG parsing test (ovstest test-conntrack ftp-alg-large-payload).
  • tests/test-conntrack.c: The PR introduces the same helper routines build_eth_ip_packet() and build_tcp_packet(), and the same test function test_ftp_alg_large_payload() that constructs a packet with a TCP payload sized such that total_size == 256 to reproduce the prior truncation, then verifies the FTP PORT command address is correctly rewritten. The content matches upstream (form-feed separators and minor whitespace are inconsequential).

Non-functional differences:

  • The upstream patch also updates AUTHORS.rst to add credit for the reporter. The PR patch does not include this, which has no impact on functionality or security.
  • The PR’s patch header includes an additional Signed-off-by (Azure Linux Security Servicing Account) and an Upstream-reference URL; these are meta changes only.

Risk assessment:

  • Low. The PR mirrors the upstream fix and tests. Changing parameter types from uint8_t to size_t in a static helper, with consistent callers, removes the integer truncation leading to potential heap overflow without altering logic elsewhere. The added test reduces regression risk by covering the edge case that previously overflowed. Context offsets differ but the surrounding functions and semantics align, indicating a clean application of the upstream change.
Raw diff (upstream vs PR)
--- upstream
+++ pr
@@ -1,289 +1,287 @@
-From 8e81545d9d1461758c72fbd08ce1f52e472bca94 Mon Sep 17 00:00:00 2001
-From: Aaron Conole <aconole@redhat.com>
-Date: Tue, 31 Mar 2026 14:39:23 +0200
-Subject: [PATCH] conntrack: Fix replace_substring to handle larger packets.
+diff --git a/SPECS/openvswitch/CVE-2026-34956.patch b/SPECS/openvswitch/CVE-2026-34956.patch
+new file mode 100644
+index 00000000000..4f6baf05c9d
+--- /dev/null
++++ b/SPECS/openvswitch/CVE-2026-34956.patch
+@@ -0,0 +1,279 @@
++From 0034c6f9a642d5462ca073b82db2ea6b6400f8a7 Mon Sep 17 00:00:00 2001
++From: Aaron Conole <aconole@redhat.com>
++Date: Tue, 31 Mar 2026 14:39:23 +0200
++Subject: [PATCH] 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 <Seiji.Sakurai@outlook.com>
++Signed-off-by: Aaron Conole <aconole@redhat.com>
++Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
++Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
++Upstream-reference: https://github.com/openvswitch/ovs/commit/8e81545d9d1461758c72fbd08ce1f52e472bca94.patch
++---
++ lib/conntrack.c        |   9 +-
++ tests/library.at       |   4 +
++ tests/test-conntrack.c | 182 +++++++++++++++++++++++++++++++++++++++++
++ 3 files changed, 192 insertions(+), 3 deletions(-)
++
++diff --git a/lib/conntrack.c b/lib/conntrack.c
++index 013709b..10ff0c7 100644
++--- a/lib/conntrack.c
+++++ b/lib/conntrack.c
++@@ -2993,9 +2993,9 @@ expectation_create(struct conntrack *ct, ovs_be16 dst_port,
++ }
++ 
++ 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);
++@@ -3013,6 +3013,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,
++diff --git a/tests/library.at b/tests/library.at
++index 7b4aceb..2079273 100644
++--- a/tests/library.at
+++++ b/tests/library.at
++@@ -306,3 +306,7 @@ AT_CHECK([ovstest test-cooperative-multitasking-nested-yield], [0], [], [dnl
++ cooperative_multitasking|ERR|Nested yield avoided, this is a bug! Enable debug logging for more details.
++ ])
++ AT_CLEANUP
+++
+++AT_SETUP([Conntrack Library - FTP ALG parsing])
+++AT_CHECK([ovstest test-conntrack ftp-alg-large-payload])
+++AT_CLEANUP
++diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c
++index 292b6c0..e59f6cc 100644
++--- a/tests/test-conntrack.c
+++++ b/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)
++ {
++@@ -251,6 +345,87 @@ test_pcap(struct ovs_cmdl_context *ctx)
++     conntrack_destroy(ct);
++     ovs_pcap_close(pcap);
++ }
+++ 
-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.
+++/* 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,
+++                      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,
+++                      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);
+++}
+++
++  
-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 <Seiji.Sakurai@outlook.com>
-Signed-off-by: Aaron Conole <aconole@redhat.com>
-Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
----
- AUTHORS.rst            |   1 +
- lib/conntrack.c        |   9 +-
- tests/library.at       |   4 +
- tests/test-conntrack.c | 182 +++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 193 insertions(+), 3 deletions(-)
-
-diff --git a/AUTHORS.rst b/AUTHORS.rst
-index fe4064ca71d..25f71fc0af3 100644
---- a/AUTHORS.rst
-+++ b/AUTHORS.rst
-@@ -728,6 +728,7 @@ Scott Hendricks
- Sean Brady                      sbrady@gtfservices.com
- Sebastian Andrzej Siewior       sebastian@breakpoint.cc
- Sébastien RICCIO                sr@swisscenter.com
-+Seiji Sakurai                   Seiji.Sakurai@outlook.com
- Shweta Seth                     shwseth@cisco.com
- Simon Jouet                     simon.jouet@gmail.com
- Spiro Kourtessis                spiro@vmware.com
-diff --git a/lib/conntrack.c b/lib/conntrack.c
-index 483da5cc934..80d2e424f42 100644
---- a/lib/conntrack.c
-+++ b/lib/conntrack.c
-@@ -3025,9 +3025,9 @@ expectation_create(struct conntrack *ct, ovs_be16 dst_port,
- }
- 
- 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);
-@@ -3045,6 +3045,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,
-diff --git a/tests/library.at b/tests/library.at
-index d962e1b3fd2..b1843298d57 100644
---- a/tests/library.at
-+++ b/tests/library.at
-@@ -308,3 +308,7 @@ AT_CHECK([ovstest test-cooperative-multitasking-nested-yield], [0], [], [dnl
- cooperative_multitasking|ERR|Nested yield avoided, this is a bug! Enable debug logging for more details.
- ])
- AT_CLEANUP
-+
-+AT_SETUP([Conntrack Library - FTP ALG parsing])
-+AT_CHECK([ovstest test-conntrack ftp-alg-large-payload])
-+AT_CLEANUP
-diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c
-index 292b6c048b8..e59f6cc790e 100644
---- a/tests/test-conntrack.c
-+++ b/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)
- {
-@@ -251,6 +345,87 @@ test_pcap(struct ovs_cmdl_context *ctx)
-     conntrack_destroy(ct);
-     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,
-+                      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,
-+                      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. */
-@@ -264,6 +439,13 @@ static const struct ovs_cmdl_command commands[] = {
-      * '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},
- };
++ static const struct ovs_cmdl_command commands[] = {
++     /* Connection tracker tests. */
++@@ -264,6 +439,13 @@ static const struct ovs_cmdl_command commands[] = {
++      * '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},
++ };
++-- 
++2.45.4
++

Verdict

CHANGES REQUESTED — Please address the issues flagged above.

Copy link
Copy Markdown
Contributor

@Kanishk-Bansal Kanishk-Bansal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch Analysis (the patch matches upstream, except the credit note author file)

  • Buddy Build 
  • patch applied during the build (check rpm.log)
  • patch include an upstream reference
  • PR has security tag

@Kanishk-Bansal Kanishk-Bansal added the ready-for-stable-review PR has passed initial review and is now ready for a second-level stable maintainer review label May 15, 2026
Copy link
Copy Markdown
Contributor

@kgodara912 kgodara912 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the test failures? Were they preexisting or caused by this patch? If they are known to be flaky, we can try to fix them in a separate PR.

level=debug msg="ERROR: 2609 tests were run,
level=debug msg="2 failed unexpectedly.
level=debug msg="4 tests were skipped.
level=debug msg="## -------------------------- ##
level=debug msg="## testsuite.log was created. ##
## -------------------------- ##

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.0-dev PRs Destined for AzureLinux 3.0 AutoPR-Security Packaging ready-for-stable-review PR has passed initial review and is now ready for a second-level stable maintainer review security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants