Skip to content

ospfd: heap-buffer-overflow in opaque LSA TLV parsers via OSPF API (ospf_te_parse_te/ri/ext_link) #21502

@shoedog

Description

@shoedog

Description

Three heap-buffer-overflow bugs exist in ospfd's opaque LSA TLV parsers (ospf_te_parse_te, ospf_te_parse_r, ospf_te_parse_ext_link in ospfd/ospf_te.c). All three fire when an opaque LSA with a body length not divisible by 4 is installed into the LSDB via the OSPF API (--apiserver).

Important: The network receive path is NOT vulnerable. ospf_packet_examin() at ospf_packet.c:2760 enforces 4-byte alignment on all incoming LSAs and drops malformed packets before processing. The vulnerability is only reachable via the OSPF API origination path, which bypasses this check.

Root cause: ospf_apiserver_opaque_lsa_new() (ospf_apiserver.c:1530) creates LSAs from the API message body without enforcing 4-byte alignment. The resulting LSA is installed via ospf_lsa_install() which dispatches to TE/SR parsers that assume 4-byte-aligned bodies.

Three confirmed OOB reads (ASAN stack traces attached):

Bug A — ospf_te_parse_ext_link (ospf_te.c:2805), opaque type 8, READ 4 bytes past 21-byte allocation
Bug B — ospf_te_parse_ri (ospf_te.c:2487), opaque type 4, READ 2 bytes past 21-byte allocation
Bug C — ospf_te_parse_te (ospf_te.c:2136), opaque type 1, READ 1 byte past 21-byte allocation

All three share the same call chain:

ospf_apiserver_originate1 (ospf_apiserver.c:1829)
  → ospf_lsa_install (ospf_lsa.c:3163)
    → hook_call(ospf_lsa_update)
      → ospf_opaque_lsa_update_hook
        → new_lsa_callback
          → ospf_mpls_te_lsa_update (ospf_te.c:3091)
            → ospf_te_parse_opaque_lsa
              → ospf_te_parse_te / ospf_te_parse_ri / ospf_te_parse_ext_link  ← OOB READ

Additional finding — SR hooks (code-confirmed): When mpls-te is disabled but SR is enabled, ospf_sr_ext_link_lsa_update (ospf_sr.c:1607) and ospf_sr_ri_lsa_update (ospf_sr.c:1426) have the same OOB pattern. These don't abort immediately — the OOB value feeds into TLV_SIZE arithmetic causing potential loop underflow and silent SR state corruption.

jopaque NULL (ospf_opaque.c:1284-1306): When a non-4-byte-aligned LSA is in the LSDB, show ip ospf database opaque-* json

passes a NULL jopaque to show functions. Daemon survives but JSON output is silently incomplete.

BUG_REPORT.md
FRR-bug-report.zip

Version

FRR Version / Commit: 10.7.0-dev (built from source, main branch)

Configure line:
./configure --enable-address-sanitizer --enable-ospfapi --enable-multipath=64 \
  --prefix=/usr/local --sysconfdir=/etc/frr --localstatedir=/var/run/frr \
  CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib"


Daemons: zebra, ospfd, mgmtd, staticd

Platform: Ubuntu 22.04 (aarch64, Lima VM on Apple M4 Pro)

How to reproduce

Network Setup:

Two-router topotest topology (r1 = API originator, r2 = peer):
r1 (10.0.0.1) ---- r2 (10.0.0.2)

r1/ospfd.conf:

router ospf
 ospf router-id 1.0.0.1
 capability opaque
 mpls-te on
 mpls-te router-address 1.0.0.1
 router-info area
 segment-routing on
 segment-routing global-block 16000 23999
 segment-routing node-msd 8
 segment-routing prefix 1.0.0.1/32 index 1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Reproduction:

Minimal reproducer (requires ospfd with --apiserver, mpls-te on):

from ospfclient import OspfOpaqueClient, LSA_TYPE_OPAQUE_AREA
import asyncio, ipaddress

async def inject():
    c = OspfOpaqueClient("localhost")
    await c.connect()
    asyncio.ensure_future(c._handle_msg_loop())
    await c.register_opaque_data_wait(LSA_TYPE_OPAQUE_AREA, 1)
    # 1-byte body → LSA length = 21 (not 4-byte aligned)
    # Note: ospfclient CLI pads to 4 bytes — must call add_opaque_data directly
    await c.add_opaque_data(ipaddress.ip_address("0.0.0.0"),
                            LSA_TYPE_OPAQUE_AREA, 1, 1, b"\xde")
    await asyncio.sleep(2)

asyncio.run(inject())

inject_otype1.py
inject_otype4.py
inject_otype8.py
raw_lsa_inject.py

Expected behavior

ospfd should reject the malformed LSA at the API ingestion point with a warning log, consistent with how ospf_packet_examin() handles the same condition on the wire receive path. No LSA with a body length not divisible by 4 should reach ospf_lsa_install().

Actual behavior

ospfd installs the malformed LSA without validation, dispatches it to the TE/SR hook callbacks, and crashes with a heap-buffer-overflow in ASAN builds. On production
builds the daemon survives but reads garbage from adjacent heap memory into TLV size arithmetic.

Backtrace (Bug C, most reproducible):

ERROR: AddressSanitizer: heap-buffer-overflow on address 0xffffa1589f34
READ of size 2 at 0xffffa1589f34 thread T0
    #0 ospf_te_parse_te ospfd/ospf_te.c:2136
    #1 ospf_te_parse_opaque_lsa ospfd/ospf_te.c:2996
    #2 ospf_mpls_te_lsa_update ospfd/ospf_te.c:3082
    #3 new_lsa_callback ospfd/ospf_opaque.c:1047
    #4 ospf_opaque_lsa_update_hook ospfd/ospf_opaque.c:1344
    #5 ospf_lsa_install ospfd/ospf_lsa.c:3163
    #6 ospf_apiserver_originate1 ospfd/ospf_apiserver.c:1829
    #7 ospf_apiserver_handle_originate_request ospfd/ospf_apiserver.c:1723
    #8 ospf_apiserver_read ospfd/ospf_apiserver.c:414


0xffffa1589f35 is located 0 bytes to the right of 21-byte region
[0xffffa1589f20,0xffffa1589f35)
allocated by thread T0 here:
    #0 __interceptor_calloc
    #1 qcalloc lib/memory.c:111
    #2 ospf_lsa_data_new ospfd/ospf_lsa.c:305
    #3 ospf_apiserver_opaque_lsa_new ospfd/ospf_apiserver.c:1586

Full ASAN log with shadow map attached frr-asan-ospfd-r1.log. Individual test logs for all three bugs attached (frr-bug-a.txt
frr-bug-b.txt
frr-bug-c.txt
).

Additional context

Suggested Fix:

Root fix in ospf_apiserver_opaque_lsa_new():

if (ntohs(data->length) % sizeof(uint32_t) != 0) {
    zlog_warn("%s: rejecting opaque LSA with non-4-byte-aligned length %u",
              __func__, ntohs(data->length));
    return NULL;
}

Defensive fix in each parser (e.g. ospf_te_parse_te):

if (len < TLV_HDR_SIZE) {
    zlog_warn("%s: LSA body too short (%u bytes) for TLV header", __func__, len);
    return -1;
}

FRR-bug-report.zip

Checklist

  • I have searched the open issues for this bug.
  • I have not included sensitive information in this report.

Metadata

Metadata

Assignees

Labels

triageNeeds further investigation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions