Version in base suite: 10.3-3 Base version: frr_10.3-3 Target version: frr_10.3-3+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/f/frr/frr_10.3-3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/f/frr/frr_10.3-3+deb13u1.dsc changelog | 18 ++ patches/CVE-2025-61099_61107.patch | 277 +++++++++++++++++++++++++++++++++++ patches/CVE-2025-61104.patch | 30 +++ patches/CVE-2026-28532.patch | 290 +++++++++++++++++++++++++++++++++++++ patches/CVE-2026-37457.patch | 34 ++++ patches/CVE-2026-37458.patch | 33 ++++ patches/CVE-2026-5107.patch | 92 +++++++++++ patches/series | 6 8 files changed, 780 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpvnyp3ctu/frr_10.3-3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpvnyp3ctu/frr_10.3-3+deb13u1.dsc: no acceptable signature found diff -Nru frr-10.3/debian/changelog frr-10.3/debian/changelog --- frr-10.3/debian/changelog 2025-05-02 02:59:37.000000000 +0000 +++ frr-10.3/debian/changelog 2026-06-02 07:30:27.000000000 +0000 @@ -1,3 +1,21 @@ +frr (10.3-3+deb13u1) trixie-security; urgency=high + + * Non-maintainer upload by the Security Team. + * Backport upstream fixes for several BGP/OSPF parsing vulnerabilities: + - CVE-2026-37457: off-by-one out-of-bounds write in the BGP FlowSpec + operator decoder (bgp_flowspec_op_decode). + - CVE-2026-28532: out-of-bounds read in OSPF TE/SR Opaque LSA TLV parsing + caused by a truncated uint16_t length accumulator. + - CVE-2026-5107: missing length validation when parsing EVPN Type-2/3/4 + and ENCAP/VNC NLRIs. + - CVE-2026-37458: missing martian next-hop validation in MP_REACH_NLRI. + - CVE-2025-61099, CVE-2025-61100, CVE-2025-61101, CVE-2025-61102, + CVE-2025-61103, CVE-2025-61104, CVE-2025-61105, CVE-2025-61106, + CVE-2025-61107: NULL pointer dereference in ospfd when dumping Opaque + LSAs while OSPF packet debugging is enabled. + + -- Aron Xu Tue, 02 Jun 2026 15:30:27 +0800 + frr (10.3-3) sid; urgency=medium * Simplifying watch file. diff -Nru frr-10.3/debian/patches/CVE-2025-61099_61107.patch frr-10.3/debian/patches/CVE-2025-61099_61107.patch --- frr-10.3/debian/patches/CVE-2025-61099_61107.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2025-61099_61107.patch 2026-06-02 07:22:46.000000000 +0000 @@ -0,0 +1,277 @@ +Description: fix NULL pointer dereference when dumping OSPF link info + When "debug ospf packet ... detail" is enabled, ospfd dumps Opaque LSAs by + calling the per-type show callbacks with a NULL vty (intending zlog output). + The TE/SR Extended-Link/Prefix dump helpers in ospf_ext.c dereferenced vty + (via vty_out) without a NULL check, so a crafted OSPF LS-Update could crash + ospfd (remote, unauthenticated DoS). This covers CVE-2025-61099, CVE-2025-61100, + CVE-2025-61101, CVE-2025-61102, CVE-2025-61103, CVE-2025-61105, CVE-2025-61106 + and CVE-2025-61107; CVE-2025-61104 is completed by the companion patch + CVE-2025-61104.patch. +Origin: backport, https://github.com/FRRouting/frr/commit/034e6fe67078810b952630055614ee5710d1196e +Bug: https://github.com/FRRouting/frr/issues/19471 +Applied-Upstream: 10.6.0 +Forwarded: not-needed +diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c +index cef27498b8..cb709f41ee 100644 +--- a/ospfd/ospf_ext.c ++++ b/ospfd/ospf_ext.c +@@ -1729,9 +1729,15 @@ static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty, + check_tlv_size(EXT_SUBTLV_RMT_ITF_ADDR_SIZE, "Remote Itf. Address"); + + if (!json) +- vty_out(vty, +- " Remote Interface Address Sub-TLV: Length %u\n Address: %pI4\n", +- ntohs(top->header.length), &top->value); ++ if (vty != NULL) { ++ vty_out(vty, ++ " Remote Interface Address Sub-TLV: Length %u\n Address: %pI4\n", ++ ntohs(top->header.length), &top->value); ++ } else { ++ zlog_debug(" Remote Interface Address Sub-TLV: Length %u", ++ ntohs(top->header.length)); ++ zlog_debug(" Address: %pI4", &top->value); ++ } + else + json_object_string_addf(json, "remoteInterfaceAddress", "%pI4", + &top->value); +@@ -1752,18 +1758,30 @@ static uint16_t show_vty_ext_link_adj_sid(struct vty *vty, + : SID_INDEX_SIZE(EXT_SUBTLV_ADJ_SID_SIZE); + check_tlv_size(tlv_size, "Adjacency SID"); + +- if (!json) +- vty_out(vty, +- " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", +- ntohs(top->header.length), top->flags, top->mtid, +- top->weight, +- CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) +- ? "Label" +- : "Index", +- CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) +- ? GET_LABEL(ntohl(top->value)) +- : ntohl(top->value)); +- else { ++ if (!json) { ++ /* Add security check for vty_out. If vty is not available, dump info via zlog.*/ ++ if (vty != NULL) ++ vty_out(vty, ++ " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", ++ ntohs(top->header.length), top->flags, top->mtid, top->weight, ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ else { ++ zlog_debug(" Adj-SID Sub-TLV: Length %u", ntohs(top->header.length)); ++ zlog_debug(" Flags: 0x%x", top->flags); ++ zlog_debug(" MT-ID:0x%x", top->mtid); ++ zlog_debug(" Weight: 0x%x", top->weight); ++ zlog_debug(" %s: %u", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ } ++ } else { + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); + json_object_string_addf(json, "weight", "0x%x", top->weight); +@@ -1791,18 +1809,32 @@ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty, + : SID_INDEX_SIZE(EXT_SUBTLV_LAN_ADJ_SID_SIZE); + check_tlv_size(tlv_size, "LAN-Adjacency SID"); + +- if (!json) +- vty_out(vty, +- " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %pI4\n\t%s: %u\n", +- ntohs(top->header.length), top->flags, top->mtid, +- top->weight, &top->neighbor_id, +- CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) +- ? "Label" +- : "Index", +- CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) +- ? GET_LABEL(ntohl(top->value)) +- : ntohl(top->value)); +- else { ++ if (!json) { ++ /* Add security check for vty_out. If vty is not available, dump info via zlog. */ ++ if (vty != NULL) { ++ vty_out(vty, ++ " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %pI4\n\t%s: %u\n", ++ ntohs(top->header.length), top->flags, top->mtid, top->weight, ++ &top->neighbor_id, ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ } else { ++ zlog_debug(" LAN-Adj-SID Sub-TLV: Length %u", ntohs(top->header.length)); ++ zlog_debug(" Flags: 0x%x", top->flags); ++ zlog_debug(" MT-ID:0x%x", top->mtid); ++ zlog_debug(" Weight: 0x%x", top->weight); ++ zlog_debug(" Neighbor ID: %pI4", &top->neighbor_id); ++ zlog_debug(" %s: %u", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ } ++ } else { + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); + json_object_string_addf(json, "weight", "0x%x", top->weight); +@@ -1823,14 +1855,23 @@ static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, + { + json_object *obj; + ++ /* Add security check for vty_out. If vty is not available, dump info via zlog. */ + if (TLV_SIZE(tlvh) > buf_size) { +- vty_out(vty, " TLV size %d exceeds buffer size. Abort!", +- TLV_SIZE(tlvh)); ++ if (vty != NULL) ++ vty_out(vty, " TLV size %d exceeds buffer size. Abort!", TLV_SIZE(tlvh)); ++ else ++ zlog_debug(" TLV size %d exceeds buffer size. Abort!", TLV_SIZE(tlvh)); ++ + return buf_size; + } + if (!json) +- vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", +- ntohs(tlvh->type), ntohs(tlvh->length)); ++ if (vty != NULL) { ++ vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", ++ ntohs(tlvh->type), ntohs(tlvh->length)); ++ } else { ++ zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]", ++ ntohs(tlvh->type), ntohs(tlvh->length)); ++ } + else { + obj = json_object_new_object(); + json_object_string_addf(obj, "type", "0x%x", +@@ -1855,19 +1896,31 @@ static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext, + + /* Verify that TLV length is valid against remaining buffer size */ + if (length > buf_size) { +- vty_out(vty, +- " Extended Link TLV size %d exceeds buffer size. Abort!\n", +- length); ++ /* Add security check for vty_out. If vty is not available, dump info via zlog. */ ++ if (vty != NULL) { ++ vty_out(vty, " Extended Link TLV size %d exceeds buffer size. Abort!\n", ++ length); ++ } else { ++ zlog_debug(" Extended Link TLV size %d exceeds buffer size. Abort!", ++ length); ++ } + return buf_size; + } + + if (!json) { +- vty_out(vty, +- " Extended Link TLV: Length %u\n Link Type: 0x%x\n" +- " Link ID: %pI4\n", +- ntohs(top->header.length), top->link_type, +- &top->link_id); +- vty_out(vty, " Link data: %pI4\n", &top->link_data); ++ /* Add security check for vty_out. If vty is not available, dump info via zlog. */ ++ if (vty != NULL) { ++ vty_out(vty, ++ " Extended Link TLV: Length %u\n Link Type: 0x%x\n" ++ " Link ID: %pI4\n", ++ ntohs(top->header.length), top->link_type, &top->link_id); ++ vty_out(vty, " Link data: %pI4\n", &top->link_data); ++ } else { ++ zlog_debug(" Extended Link TLV: Length %u", ntohs(top->header.length)); ++ zlog_debug(" Link Type: 0x%x", top->link_type); ++ zlog_debug(" Link ID: %pI4", &top->link_id); ++ zlog_debug(" Link data: %pI4", &top->link_data); ++ } + } else { + json_object_string_addf(json, "linkType", "0x%x", + top->link_type); +@@ -1959,18 +2012,29 @@ static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty, + : SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE); + check_tlv_size(tlv_size, "Prefix SID"); + +- if (!json) +- vty_out(vty, +- " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", +- ntohs(top->header.length), top->algorithm, top->flags, +- top->mtid, +- CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) +- ? "Label" +- : "Index", +- CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) +- ? GET_LABEL(ntohl(top->value)) +- : ntohl(top->value)); +- else { ++ if (!json) { ++ if (vty != NULL) { ++ vty_out(vty, ++ " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", ++ ntohs(top->header.length), top->algorithm, top->flags, top->mtid, ++ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ } else { ++ zlog_debug(" Prefix SID Sub-TLV: Length %u", ntohs(top->header.length)); ++ zlog_debug(" Algorithm: %u", top->algorithm); ++ zlog_debug(" Flags: 0x%x", top->flags); ++ zlog_debug(" MT-ID:0x%x", top->mtid); ++ zlog_debug(" %s: %u", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label" ++ : "Index", ++ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ++ ? GET_LABEL(ntohl(top->value)) ++ : ntohl(top->value)); ++ } ++ } else { + json_object_int_add(json, "algorithm", top->algorithm); + json_object_string_addf(json, "flags", "0x%x", top->flags); + json_object_string_addf(json, "mtID", "0x%x", top->mtid); +@@ -1995,19 +2059,31 @@ static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext, + + /* Verify that TLV length is valid against remaining buffer size */ + if (length > buf_size) { +- vty_out(vty, +- " Extended Link TLV size %d exceeds buffer size. Abort!\n", +- length); ++ if (vty != NULL) { ++ vty_out(vty, " Extended Link TLV size %d exceeds buffer size. Abort!\n", ++ length); ++ } else { ++ zlog_debug(" Extended Link TLV size %d exceeds buffer size. Abort!", ++ length); ++ } + return buf_size; + } + +- if (!json) +- vty_out(vty, +- " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n" +- "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %pI4/%u\n", +- ntohs(top->header.length), top->route_type, top->af, +- top->flags, &top->address, top->pref_length); +- else { ++ if (!json) { ++ if (vty != NULL) { ++ vty_out(vty, ++ " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n" ++ "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %pI4/%u\n", ++ ntohs(top->header.length), top->route_type, top->af, top->flags, ++ &top->address, top->pref_length); ++ } else { ++ zlog_debug(" Extended Prefix TLV: Length %u", ntohs(top->header.length)); ++ zlog_debug(" Route Type: %u", top->route_type); ++ zlog_debug(" Address Family: 0x%x", top->af); ++ zlog_debug(" Flags: 0x%x", top->flags); ++ zlog_debug(" Address: %pI4/%u", &top->address, top->pref_length); ++ } ++ } else { + json_object_int_add(json, "routeType", top->route_type); + json_object_string_addf(json, "addressFamily", "0x%x", top->af); + json_object_string_addf(json, "flags", "0x%x", top->flags); diff -Nru frr-10.3/debian/patches/CVE-2025-61104.patch frr-10.3/debian/patches/CVE-2025-61104.patch --- frr-10.3/debian/patches/CVE-2025-61104.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2025-61104.patch 2026-06-02 07:22:46.000000000 +0000 @@ -0,0 +1,30 @@ +Description: add NULL check for vty_out in check_tlv_size + Companion to CVE-2025-61099.patch: guard the vty_out() call in check_tlv_size() + in ospf_ext.c so the OSPF Opaque LSA detail dump path does not dereference a + NULL vty (CVE-2025-61104). +Origin: backport, https://github.com/FRRouting/frr/commit/b7d9b7aa47627b31e4b50795284408ab6de98660 +Bug: https://github.com/FRRouting/frr/issues/19471 +Applied-Upstream: 10.6.0 +Forwarded: not-needed +diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c +index c80ad636a1..cef27498b8 100644 +--- a/ospfd/ospf_ext.c ++++ b/ospfd/ospf_ext.c +@@ -1705,11 +1705,15 @@ static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op) + * ------------------------------------ + */ + ++/* Check NULL for vty. If vty is not available, dump info via zlog */ + #define check_tlv_size(size, msg) \ + do { \ + if (ntohs(tlvh->length) != size) { \ +- vty_out(vty, " Wrong %s TLV size: %d(%d). Abort!\n", \ +- msg, ntohs(tlvh->length), size); \ ++ if (vty != NULL) \ ++ vty_out(vty, " Wrong %s TLV size: %d(%d). Abort!\n", \ ++ msg, ntohs(tlvh->length), size); \ ++ else \ ++ zlog_debug(" Wrong %s TLV size: %d(%d). Abort!", msg, ntohs(tlvh->length), size); \ + return size + TLV_HDR_SIZE; \ + } \ + } while (0) diff -Nru frr-10.3/debian/patches/CVE-2026-28532.patch frr-10.3/debian/patches/CVE-2026-28532.patch --- frr-10.3/debian/patches/CVE-2026-28532.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2026-28532.patch 2026-06-02 07:22:46.000000000 +0000 @@ -0,0 +1,290 @@ +Description: harden OSPF TE/SR TLV iteration against malformed lengths + A uint16_t accumulator truncated uint32_t TLV_SIZE() values in the OSPF TE/SR + TLV parsers (ospf_te.c, ospf_sr.c), so a crafted Opaque LSA with malformed TLV + lengths could make the loop terminator fail while the read pointer advanced, + causing an out-of-bounds read / ospfd crash (CVE-2026-28532). +Origin: backport, https://github.com/FRRouting/frr/commit/31dd5cb832 +Bug: https://github.com/FRRouting/frr/pull/21015 +Applied-Upstream: 10.3.4 +Forwarded: not-needed +diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c +index 89db97922f..fefb177937 100644 +--- a/ospfd/ospf_sr.c ++++ b/ospfd/ospf_sr.c +@@ -985,7 +985,8 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh, size_t size) + struct ext_subtlv_rmt_itf_addr *rmt_itf; + + struct tlv_header *sub_tlvh; +- uint16_t length = 0, sum = 0, i = 0; ++ uint32_t length = 0, sum = 0; ++ uint16_t i = 0; + + /* Check TLV size */ + if ((ntohs(tlvh->length) > size) +@@ -1000,7 +1001,15 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh, size_t size) + length = ntohs(tlvh->length) - EXT_TLV_LINK_SIZE; + sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); +- for (; sum < length && sub_tlvh; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) { ++ for (; sum < length && sub_tlvh;) { ++ uint32_t tlv_size = TLV_SIZE(sub_tlvh); ++ ++ if (tlv_size > length - sum) { ++ zlog_warn("Malformed Extended Link sub-TLV size %u (remaining %u)", ++ tlv_size, length - sum); ++ break; ++ } ++ + switch (ntohs(sub_tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + adj_sid = (struct ext_subtlv_adj_sid *)sub_tlvh; +@@ -1041,7 +1050,9 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh, size_t size) + default: + break; + } +- sum += TLV_SIZE(sub_tlvh); ++ sum += tlv_size; ++ if (sum < length) ++ sub_tlvh = TLV_HDR_NEXT(sub_tlvh); + } + + IPV4_ADDR_COPY(&srl->itf_addr, &link->link_data); +@@ -1062,7 +1073,7 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh, + struct ext_subtlv_prefix_sid *psid; + + struct tlv_header *sub_tlvh; +- uint16_t length = 0, sum = 0; ++ uint32_t length = 0, sum = 0; + + /* Check TLV size */ + if ((ntohs(tlvh->length) > size) +@@ -1077,7 +1088,15 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh, + length = ntohs(tlvh->length) - EXT_TLV_PREFIX_SIZE; + sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE + + EXT_TLV_PREFIX_SIZE); +- for (; sum < length && sub_tlvh; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) { ++ for (; sum < length && sub_tlvh;) { ++ uint32_t tlv_size = TLV_SIZE(sub_tlvh); ++ ++ if (tlv_size > length - sum) { ++ zlog_warn("Malformed Extended Prefix sub-TLV size %u (remaining %u)", ++ tlv_size, length - sum); ++ break; ++ } ++ + switch (ntohs(sub_tlvh->type)) { + case EXT_SUBTLV_PREFIX_SID: + psid = (struct ext_subtlv_prefix_sid *)sub_tlvh; +@@ -1102,7 +1121,9 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh, + default: + break; + } +- sum += TLV_SIZE(sub_tlvh); ++ sum += tlv_size; ++ if (sum < length) ++ sub_tlvh = TLV_HDR_NEXT(sub_tlvh); + } + + osr_debug(" |- Found SID %u for prefix %pFX", srp->sid, +@@ -1370,7 +1391,7 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) + struct ri_sr_tlv_sid_label_range *ri_srlb = NULL; + struct ri_sr_tlv_sr_algorithm *algo = NULL; + struct sr_block srgb; +- uint16_t length = 0, sum = 0; ++ uint32_t length = 0, sum = 0; + uint8_t msd = 0; + + osr_debug("SR (%s): Process Router Information LSA 4.0.0.%u from %pI4", +@@ -1398,8 +1419,15 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) + srgb.range_size = 0; + srgb.lower_bound = 0; + +- for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL); +- tlvh = TLV_HDR_NEXT(tlvh)) { ++ for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL);) { ++ uint32_t tlv_size = TLV_SIZE(tlvh); ++ ++ if (tlv_size > length - sum) { ++ zlog_warn("Malformed RI TLV size %u (remaining %u)", tlv_size, ++ length - sum); ++ break; ++ } ++ + switch (ntohs(tlvh->type)) { + case RI_SR_TLV_SR_ALGORITHM: + algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; +@@ -1416,7 +1444,9 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) + default: + break; + } +- sum += TLV_SIZE(tlvh); ++ sum += tlv_size; ++ if (sum < length) ++ tlvh = TLV_HDR_NEXT(tlvh); + } + + /* Check if Segment Routing Capabilities has been found */ +diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c +index d57990e1a1..3787c05ccf 100644 +--- a/ospfd/ospf_te.c ++++ b/ospfd/ospf_te.c +@@ -2122,7 +2122,7 @@ static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) + struct ls_attributes *old, attr = {}; + struct tlv_header *tlvh; + void *value; +- uint16_t len, sum; ++ uint32_t len, sum; + uint8_t lsa_id; + + /* Initialize Attribute */ +@@ -2152,11 +2152,19 @@ static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) + + sum = sizeof(struct tlv_header); + /* Browse sub-TLV and fulfill Link State Attributes */ +- for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { ++ tlvh = TLV_DATA(tlvh); ++ while (sum < len) { ++ uint32_t tlv_size = TLV_SIZE(tlvh); + uint32_t val32, tab32[2]; + float valf, tabf[8]; + struct in_addr addr; + ++ if (tlv_size > len - sum) { ++ zlog_warn("Malformed TE sub-TLV size %u (remaining %u)", tlv_size, ++ len - sum); ++ break; ++ } ++ + value = TLV_DATA(tlvh); + switch (ntohs(tlvh->type)) { + case TE_LINK_SUBTLV_LCLIF_IPADDR: +@@ -2251,7 +2259,9 @@ static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) + default: + break; + } +- sum += TLV_SIZE(tlvh); ++ sum += tlv_size; ++ if (sum < len) ++ tlvh = TLV_HDR_NEXT(tlvh); + } + + /* Get corresponding Edge from Link State Data Base */ +@@ -2351,7 +2361,7 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) + struct tlv_header *tlvh; + struct in_addr addr; + struct ls_edge_key key = {.family = AF_UNSPEC}; +- uint16_t len, sum; ++ uint32_t len, sum; + uint8_t lsa_id; + + /* Initialize TLV browsing */ +@@ -2363,14 +2373,25 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) + sum = sizeof(struct tlv_header); + + /* Browse sub-TLV to find Link ID */ +- for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { ++ tlvh = TLV_DATA(tlvh); ++ while (sum < len) { ++ uint32_t tlv_size = TLV_SIZE(tlvh); ++ ++ if (tlv_size > len - sum) { ++ zlog_warn("Malformed TE sub-TLV size %u (remaining %u)", tlv_size, ++ len - sum); ++ break; ++ } ++ + if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) { + memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE); + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &addr); + break; + } +- sum += TLV_SIZE(tlvh); ++ sum += tlv_size; ++ if (sum < len) ++ tlvh = TLV_HDR_NEXT(tlvh); + } + if (key.family == AF_UNSPEC) + return 0; +@@ -2445,7 +2466,7 @@ static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa) + struct ls_node *node; + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; +- uint16_t len = 0, sum = 0; ++ uint32_t len = 0, sum = 0; + + /* Get vertex / Node from LSA Advertised Router ID */ + vertex = get_vertex(ted, lsa); +@@ -2456,13 +2477,18 @@ static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa) + + /* Initialize TLV browsing */ + len = lsa->size - OSPF_LSA_HEADER_SIZE; +- for (tlvh = TLV_HDR_TOP(lsah); sum < len && tlvh; +- tlvh = TLV_HDR_NEXT(tlvh)) { ++ for (tlvh = TLV_HDR_TOP(lsah); sum < len && tlvh;) { ++ uint32_t tlv_size = TLV_SIZE(tlvh); + struct ri_sr_tlv_sr_algorithm *algo; + struct ri_sr_tlv_sid_label_range *range; + struct ri_sr_tlv_node_msd *msd; + uint32_t size, lower; + ++ if (tlv_size > len - sum) { ++ zlog_warn("Malformed RI TLV size %u (remaining %u)", tlv_size, len - sum); ++ break; ++ } ++ + switch (ntohs(tlvh->type)) { + case RI_SR_TLV_SR_ALGORITHM: + if (TLV_BODY_SIZE(tlvh) < 1 || +@@ -2547,7 +2573,9 @@ static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa) + default: + break; + } +- sum += TLV_SIZE(tlvh); ++ sum += tlv_size; ++ if (sum < len) ++ tlvh = TLV_HDR_NEXT(tlvh); + } + + /* Vertex has been created or updated: export it */ +@@ -2759,7 +2787,8 @@ static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) + struct ext_tlv_link *ext; + struct ls_edge *edge; + struct ls_attributes *atr; +- uint16_t len = 0, sum = 0, i; ++ uint32_t len = 0, sum = 0; ++ uint16_t i; + uint32_t label; + + /* Get corresponding Edge from Link State Data Base */ +@@ -2793,11 +2822,18 @@ static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) + len -= EXT_TLV_LINK_SIZE; + tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); +- for (; sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { ++ for (; sum < len;) { ++ uint32_t tlv_size = TLV_SIZE(tlvh); + struct ext_subtlv_adj_sid *adj; + struct ext_subtlv_lan_adj_sid *ladj; + struct ext_subtlv_rmt_itf_addr *rmt; + ++ if (tlv_size > len - sum) { ++ zlog_warn("Malformed Extended Link sub-TLV size %u (remaining %u)", ++ tlv_size, len - sum); ++ break; ++ } ++ + switch (ntohs(tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + if (TLV_BODY_SIZE(tlvh) != EXT_SUBTLV_ADJ_SID_SIZE) +@@ -2876,7 +2912,9 @@ static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) + default: + break; + } +- sum += TLV_SIZE(tlvh); ++ sum += tlv_size; ++ if (sum < len) ++ tlvh = TLV_HDR_NEXT(tlvh); + } + + /* Export Link State Edge if needed */ diff -Nru frr-10.3/debian/patches/CVE-2026-37457.patch frr-10.3/debian/patches/CVE-2026-37457.patch --- frr-10.3/debian/patches/CVE-2026-37457.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2026-37457.patch 2026-06-02 07:29:25.000000000 +0000 @@ -0,0 +1,34 @@ +Description: fix off-by-one out-of-bounds write in FlowSpec operator array + An off-by-one error in the FlowSpec operator decoding (bgp_flowspec_op_decode / + bgp_flowspec_bitmask_decode) allows a crafted FlowSpec NLRI with more than the + maximum number of chained operators to write one element past the mval[] array, + leading to a stack out-of-bounds write (CVE-2026-37457). + . + Adapted from upstream commit 0e6882bc72 / stable backport bc106c9fa9 to the + 10.3 release tree: op_decode lacked the early return, and bitmask_decode already + had the braces+return but used the wrong (>) comparison. +Origin: backport, https://github.com/FRRouting/frr/commit/0e6882bc72c0278988a47b2f0f73b7a91099a25c +Bug: https://github.com/FRRouting/frr/pull/21054 +Forwarded: not-needed +--- a/bgpd/bgp_flowspec_util.c ++++ b/bgpd/bgp_flowspec_util.c +@@ -256,7 +256,9 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, + *error = 0; + do { +- if (loop > BGP_PBR_MATCH_VAL_MAX) +- *error = -2; ++ if (loop >= BGP_PBR_MATCH_VAL_MAX) { ++ *error = -2; ++ return offset; ++ } + hex2bin(&nlri_ptr[offset], op); + offset++; + len = 2*op[2]+op[3]; +@@ -360,6 +362,6 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, + *error = 0; + do { +- if (loop > BGP_PBR_MATCH_VAL_MAX) { ++ if (loop >= BGP_PBR_MATCH_VAL_MAX) { + *error = -2; + return offset; + } diff -Nru frr-10.3/debian/patches/CVE-2026-37458.patch frr-10.3/debian/patches/CVE-2026-37458.patch --- frr-10.3/debian/patches/CVE-2026-37458.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2026-37458.patch 2026-06-02 07:22:46.000000000 +0000 @@ -0,0 +1,33 @@ +Description: validate MP_REACH_NLRI attribute against incorrect next-hop + The IPv4 next-hop carried in an MP_REACH_NLRI attribute was not validated, so + a martian next-hop was accepted and could cause a denial of service. Reject the + attribute (treat-as-withdraw) when the next-hop is a martian address, unless + the operator explicitly allowed martian next-hops (CVE-2026-37458). + . + Backported from the upstream master commit; there is no upstream stable/10.3 + backport. allow_martian exists in 10.3, so the upstream change applies directly. +Origin: backport, https://github.com/FRRouting/frr/commit/8102a8aeceb9f86fdfe1f80cd77080522bab69c8 +Bug: https://github.com/FRRouting/frr/pull/21075 +Forwarded: not-needed +diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c +index bfc6e520ca..8430a746d7 100644 +--- a/bgpd/bgp_attr.c ++++ b/bgpd/bgp_attr.c +@@ -2747,6 +2747,17 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, + fallthrough; + case BGP_ATTR_NHLEN_IPV4: + stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN); ++ ++ /* We do already the same validation for NEXT_HOP attribute, ++ * so let's do it here as well for consistency and to avoid potential ++ * security issues with martian addresses in MP_REACH_NLRI. ++ */ ++ if (ipv4_martian(&attr->mp_nexthop_global_in) && !peer->bgp->allow_martian) { ++ zlog_warn("%s sent martian nexthop %pI4 in MP_REACH_NLRI", peer->host, ++ &attr->mp_nexthop_global_in); ++ return BGP_ATTR_PARSE_WITHDRAW; ++ } ++ + /* Probably needed for RFC 2283 */ + if (attr->nexthop.s_addr == INADDR_ANY) + memcpy(&attr->nexthop.s_addr, diff -Nru frr-10.3/debian/patches/CVE-2026-5107.patch frr-10.3/debian/patches/CVE-2026-5107.patch --- frr-10.3/debian/patches/CVE-2026-5107.patch 1970-01-01 00:00:00.000000000 +0000 +++ frr-10.3/debian/patches/CVE-2026-5107.patch 2026-06-02 07:22:46.000000000 +0000 @@ -0,0 +1,92 @@ +Description: improve packet parsing for EVPN and ENCAP/VNC + Missing validation of the EVPN NLRI ipaddr_len against psize in the EVPN + Type-2/3/4 route handlers (and a VNC/ENCAP sub-TLV length check) allowed + crafted BGP UPDATE messages to manipulate routes or trigger a limited DoS + (CVE-2026-5107). +Origin: backport, https://github.com/FRRouting/frr/commit/59d4c4a0b8 +Bug: https://github.com/FRRouting/frr/pull/21237 +Applied-Upstream: 10.3.4 +Forwarded: not-needed +diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c +index b768700b3d..609b180301 100644 +--- a/bgpd/bgp_evpn.c ++++ b/bgpd/bgp_evpn.c +@@ -4956,6 +4956,14 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, + goto fail; + } + ++ /* Validate ipaddr_len against the NLRI length */ ++ if ((psize != 33 + (ipaddr_len / 8)) && (psize != 36 + (ipaddr_len / 8))) { ++ flog_err(EC_BGP_EVPN_ROUTE_INVALID, ++ "%u:%s - Rx EVPN Type-2 NLRI with invalid IP address length %d", ++ peer->bgp->vrf_id, peer->host, ipaddr_len); ++ goto fail; ++ } ++ + if (ipaddr_len) { + ipaddr_len /= 8; /* Convert to bytes. */ + p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) +@@ -5053,6 +5061,15 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, + + /* Get the IP. */ + ipaddr_len = *pfx++; ++ ++ /* Validate */ ++ if (psize != 13 + (ipaddr_len / 8)) { ++ flog_err(EC_BGP_EVPN_ROUTE_INVALID, ++ "%u:%s - Rx EVPN Type-3 NLRI with invalid IP address length %d", ++ peer->bgp->vrf_id, peer->host, ipaddr_len); ++ return -1; ++ } ++ + if (ipaddr_len == IPV4_MAX_BITLEN) { + p.prefix.imet_addr.ip.ipa_type = IPADDR_V4; + memcpy(&p.prefix.imet_addr.ip.ip.addr, pfx, IPV4_MAX_BYTELEN); +diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c +index 2a5588a81b..a04cbfff99 100644 +--- a/bgpd/bgp_evpn_mh.c ++++ b/bgpd/bgp_evpn_mh.c +@@ -753,9 +753,17 @@ int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, + memcpy(&esi, pfx, ESI_BYTES); + pfx += ESI_BYTES; + +- + /* Get the IP. */ + ipaddr_len = *pfx++; ++ ++ /* Validate */ ++ if (psize != 19 + (ipaddr_len / 8)) { ++ flog_err(EC_BGP_EVPN_ROUTE_INVALID, ++ "%u:%s - Rx EVPN Type-4 NLRI with invalid IP address length %d", ++ peer->bgp->vrf_id, peer->host, ipaddr_len); ++ return -1; ++ } ++ + if (ipaddr_len == IPV4_MAX_BITLEN) { + memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN); + } else { +diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c +index 53e416b2ee..ec9e35d4fd 100644 +--- a/bgpd/rfapi/rfapi_rib.c ++++ b/bgpd/rfapi/rfapi_rib.c +@@ -631,11 +631,20 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, + break; + + case BGP_VNC_SUBTLV_TYPE_RFPOPTION: ++ /* Check for short subtlv: drop */ ++ if (pEncap->length < 3) ++ break; ++ ++ /* Length of zero not valid */ ++ if (pEncap->value[1] == 0) ++ break; ++ + hop = XCALLOC(MTYPE_BGP_TEA_OPTIONS, + sizeof(struct bgp_tea_options)); + assert(hop); + hop->type = pEncap->value[0]; + hop->length = pEncap->value[1]; ++ + hop->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE, + pEncap->length - 2); + assert(hop->value); diff -Nru frr-10.3/debian/patches/series frr-10.3/debian/patches/series --- frr-10.3/debian/patches/series 2025-04-21 07:52:29.000000000 +0000 +++ frr-10.3/debian/patches/series 2026-06-02 07:30:27.000000000 +0000 @@ -1 +1,7 @@ upstream/0001-fix-ftbfs-s390x.patch +CVE-2026-37457.patch +CVE-2026-28532.patch +CVE-2026-5107.patch +CVE-2026-37458.patch +CVE-2025-61099_61107.patch +CVE-2025-61104.patch