home intel cve-2026-20401-mediatek-modem-uncaught-exception-dos
CVE Analysis 2026-02-02 · 9 min read

CVE-2026-20401: MediaTek Modem Uncaught Exception Remote DoS

A missing exception handler in MediaTek's modem stack allows a rogue base station to crash any connected UE remotely. No privileges or user interaction required.

#modem-vulnerability#denial-of-service#remote-code-execution#base-station-spoofing#exception-handling
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-20401 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-20401HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-20401 is a remote denial-of-service vulnerability in the MediaTek modem firmware stack. A rogue LTE/NR base station can transmit a crafted over-the-air message that triggers an unhandled C++ exception inside the modem's protocol processing layer, causing an unrecoverable crash of the modem subsystem. Because the modem runs as a privileged co-processor with its own RTOS, the crash propagates as a full device reset or persistent modem-dead state on the host Android system.

Patch ID MOLY01738310, Issue MSV-5933, was shipped in MediaTek's February 2026 Product Security Bulletin. CVSS 7.5 (HIGH) — Network / Low complexity / No privileges / No user interaction / No scope change / High availability impact.

Root cause: A modem RRC/NAS message parser directly invokes a method on a potentially-null or malformed protocol object without wrapping the call in a top-level exception handler, so a crafted OTA message that produces an unexpected object state causes an uncaught std::exception that terminates the modem task.

Affected Component

The vulnerability lives in the MOLY modem firmware image — MediaTek's proprietary RTOS-based baseband software that runs on the modem DSP/MCU subsystem alongside chips from the Dimensity, Helio, and legacy MT series. The affected layer sits within the RRC (Radio Resource Control) or NAS (Non-Access Stratum) protocol handler, both of which process base-station-originated messages directly from the air interface before any authentication or integrity verification completes. The host Android OS has no visibility into nor control over these handlers at the time of exploitation.

  • Firmware component: modem/moly/ — RRC/NAS message dispatch
  • Patch ID: MOLY01738310
  • Issue tracker: MSV-5933
  • Affected chipsets: Per MediaTek February 2026 bulletin (see NVD)

Root Cause Analysis

MediaTek's MOLY modem stack is written primarily in C++ and compiled for the modem MCU (typically an ARM Cortex-R or Xtensa core). Protocol message objects are heap-allocated per-session and dispatched through a virtual function table. The bug is in the message dispatch path: when a malformed or unexpected IE (Information Element) arrives in an RRC connection setup or NAS mobility management message, the parser constructs a protocol object in a partially-initialized state. The dispatcher then calls a virtual method on that object — specifically through a vtable pointer that may be null or point to an unexpected handler — without any surrounding try/catch block at the task level.

Below is reconstructed pseudocode based on the vulnerability class and patch identifier patterns common to MOLY RRC handlers:


// modem/moly/protocol/rrc/rrc_msg_handler.cpp
// Reconstructed from crash pattern and patch delta MOLY01738310

void rrc_dispatch_dl_message(rrc_context_t *ctx, uint8_t *ie_buf, uint32_t ie_len) {
    rrc_msg_obj_t *msg = rrc_parse_ie(ie_buf, ie_len);
    // BUG: rrc_parse_ie() returns partially-initialized object on malformed IE;
    //      msg->vtable may be null or msg itself may be null if optional IE
    //      triggers an unexpected code path. No null/validity check here.

    msg->process(ctx);   // BUG: uncaught exception if msg is invalid object;
                         //      std::bad_function_call or access violation
                         //      propagates with no top-level handler in task loop
}

rrc_msg_obj_t *rrc_parse_ie(uint8_t *buf, uint32_t len) {
    rrc_msg_obj_t *obj = new rrc_msg_obj_t();

    uint8_t ie_type = buf[0];
    switch (ie_type) {
        case RRC_IE_CONN_SETUP:
            obj->init_conn_setup(buf + 1, len - 1);
            break;
        case RRC_IE_MOBILITY_CMD:
            obj->init_mobility(buf + 1, len - 1);
            break;
        default:
            // BUG: unrecognized IE type — obj is allocated but never fully
            //      initialized; vtable ptr remains at base class default which
            //      has no valid process() implementation for this context.
            break;   // returns half-constructed object to caller
    }
    return obj;  // caller assumes fully valid object
}

The modem task scheduler in MOLY does not install a top-level std::terminate handler or catch-all exception handler around the message dispatch loop. When msg->process(ctx) fires a std::bad_function_call or triggers a null-pointer dereference that the ARM exception model converts to a C++ exception, the RTOS task terminates abnormally. The modem watchdog detects the dead task and triggers a full modem reset.

Memory Layout


// rrc_msg_obj_t — heap layout on modem MCU (ARM Cortex-R, 32-bit)
struct rrc_msg_obj_t {
    /* +0x00 */ void        **vtable;       // C++ vtable ptr — NULL if not init'd
    /* +0x04 */ uint8_t       ie_type;      // IE discriminator byte
    /* +0x05 */ uint8_t       state;        // parse state: 0=uninit, 1=partial, 2=ready
    /* +0x06 */ uint16_t      ie_len;       // length of parsed IE payload
    /* +0x08 */ uint8_t      *ie_payload;   // pointer to parsed IE data
    /* +0x0C */ uint32_t      session_id;   // RRC session context
    /* +0x10 */ rrc_context_t *parent_ctx;  // back-pointer to owning context
    /* +0x14 */ uint32_t      reserved;     // padding
};
// sizeof(rrc_msg_obj_t) = 0x18

MODEM HEAP STATE — NORMAL (valid IE, fully initialized):
  [0x200A1000] rrc_msg_obj_t {
      vtable   = 0x10034A80   <- points to rrc_conn_setup_vtbl
      ie_type  = 0x01         <- RRC_IE_CONN_SETUP
      state    = 0x02         <- READY
      ie_len   = 0x0034
      ie_payload = 0x200A1020
      session_id = 0xDEAD0001
  }
  -> msg->process(ctx) dispatches through vtable[2] — SUCCESS

MODEM HEAP STATE — MALFORMED IE (attacker-crafted, unknown ie_type):
  [0x200A1000] rrc_msg_obj_t {
      vtable   = 0x10034B00   <- base class vtable; process() = pure virtual
      ie_type  = 0xFF         <- unrecognized, hits default: branch
      state    = 0x00         <- UNINIT — init_*() was never called
      ie_len   = 0x0000
      ie_payload = 0x00000000 <- NULL
      session_id = 0x00000000
  }
  -> msg->process(ctx) calls pure virtual -> std::terminate()
  -> MOLY task KILLED by RTOS watchdog
  -> Host sees: modem-dead / radio interface down / device reboot

Exploitation Mechanics


EXPLOIT CHAIN — CVE-2026-20401 Remote Modem DoS:

1. ATTACKER SETUP
   Deploy software-defined radio (e.g., srsRAN, OsmocomBB) configured as
   rogue eNB/gNB broadcasting a stronger signal than the legitimate network.
   Target UE camps on attacker's cell during normal scanning (no auth needed).

2. RRC CONNECTION ESTABLISHMENT
   Attacker allows UE to initiate RRC Connection Request — normal protocol flow.
   At this stage, UE processes DL messages from base station without NAS-level
   integrity protection being established yet.

3. MALFORMED IE INJECTION
   Attacker sends RRC Connection Setup or DL-DCCH message containing a crafted
   Information Element with ie_type = 0xFF (or any value not in the handled
   switch cases of rrc_parse_ie()).
   
   Crafted RRC PDU (hex excerpt):
   00 FF 00 00 00 00 ...   <- ie_type=0xFF, rest zeroed

4. PARSER RETURNS HALF-CONSTRUCTED OBJECT
   rrc_parse_ie() allocates rrc_msg_obj_t, hits default: branch, returns
   object with state=UNINIT and vtable pointing to pure-virtual base.

5. VIRTUAL DISPATCH FIRES std::terminate
   rrc_dispatch_dl_message() calls msg->process(ctx) with no exception guard.
   Pure virtual call invokes __cxa_pure_virtual() -> std::terminate() ->
   MOLY RTOS kills the RRC task.

6. WATCHDOG ESCALATION
   Modem watchdog timer detects dead RRC task within ~2 seconds.
   Triggers modem subsystem reset: CP_RESET signal asserted to AP.

7. HOST IMPACT
   Android RILJ receives modem-dead unsolicited response.
   Device enters no-service state or triggers full system reboot depending
   on OEM policy. Attack repeatable — UE will re-camp on rogue cell after reset.

REPEATABILITY: Attacker can loop steps 2-7 indefinitely, maintaining a
persistent denial-of-service for any UE within RF range.

Patch Analysis

Patch MOLY01738310 addresses the vulnerability at two levels: validating the parsed object state before dispatch, and installing a catch-all exception boundary around the task's main message processing loop.


// BEFORE (vulnerable) — rrc_msg_handler.cpp, pre-MOLY01738310:

rrc_msg_obj_t *rrc_parse_ie(uint8_t *buf, uint32_t len) {
    rrc_msg_obj_t *obj = new rrc_msg_obj_t();
    uint8_t ie_type = buf[0];
    switch (ie_type) {
        case RRC_IE_CONN_SETUP:   obj->init_conn_setup(buf+1, len-1); break;
        case RRC_IE_MOBILITY_CMD: obj->init_mobility(buf+1, len-1);   break;
        default: break;  // returns uninit object
    }
    return obj;
}

void rrc_dispatch_dl_message(rrc_context_t *ctx, uint8_t *ie_buf, uint32_t ie_len) {
    rrc_msg_obj_t *msg = rrc_parse_ie(ie_buf, ie_len);
    msg->process(ctx);  // no guard — exception kills task
}


// AFTER (patched) — MOLY01738310:

rrc_msg_obj_t *rrc_parse_ie(uint8_t *buf, uint32_t len) {
    rrc_msg_obj_t *obj = new rrc_msg_obj_t();
    uint8_t ie_type = buf[0];
    switch (ie_type) {
        case RRC_IE_CONN_SETUP:   obj->init_conn_setup(buf+1, len-1); break;
        case RRC_IE_MOBILITY_CMD: obj->init_mobility(buf+1, len-1);   break;
        default:
            delete obj;
            return nullptr;  // FIX: explicit null on unrecognized IE
    }
    return obj;
}

void rrc_dispatch_dl_message(rrc_context_t *ctx, uint8_t *ie_buf, uint32_t ie_len) {
    rrc_msg_obj_t *msg = rrc_parse_ie(ie_buf, ie_len);

    // FIX 1: null check before dispatch
    if (msg == nullptr) {
        RRC_LOG_WARN("rrc_dispatch_dl_message: unrecognized IE, dropping");
        return;
    }

    // FIX 2: exception boundary prevents task termination
    try {
        msg->process(ctx);
    } catch (const std::exception &e) {
        RRC_LOG_ERROR("rrc_dispatch_dl_message: caught exception: %s", e.what());
        // log and discard; task continues running
    } catch (...) {
        RRC_LOG_ERROR("rrc_dispatch_dl_message: caught unknown exception");
    }

    delete msg;
}

The two-pronged fix is correct: returning nullptr on unrecognized IE types prevents the half-constructed object from ever reaching the dispatcher, while the try/catch boundary provides defense-in-depth for any future unhandled exception class that bypasses the null check. A more robust long-term fix would enforce that all concrete rrc_msg_obj_t subclasses override process() and that the base class implementation is non-pure with a safe no-op default.

Detection and Indicators

On Android devices with MediaTek modems, a successful exploit produces observable artifacts:

  • Logcat: RILJ: modem crashed, trigger reset or RILD: CP_RESET received repeating in short intervals
  • Kernel log (dmesg): ccci_fsm: MD exception occur — MediaTek CCCI (Cross-Core Communication Interface) exception flag
  • ADB bugreport: /data/mdlog/ will contain modem crash dump files (mdm_*.bin) with timestamps correlating to the attack window
  • Radio state: getprop gsm.modem.status returns RESET or EXCEPTION during/after attack
  • Network side: Repeated RRC Connection Request messages from the same IMSI within short windows on eNB logs, indicating the UE is repeatedly crashing and re-attaching

EXAMPLE MODEM CRASH LOG (mdm_exception_log.txt):
[00234.112] RRC_TASK: unhandled exception in message dispatch
[00234.112] PC=0x10034B04 LR=0x10021A3C SP=0x200BFF80
[00234.113] ASSERT: __cxa_pure_virtual called
[00234.113] MOLY watchdog: RRC task dead, initiating CP_RESET
[00234.891] CCCI: MD exception, host notified

Network-level detection: monitor for rogue cells broadcasting with unusually high signal strength (RSRP > -70 dBm) in areas where it is unexpected, combined with high UE re-attach rates. IMSI-catchers used for this attack will be detectable by IMSI-catcher-catcher tooling (e.g., SnoopSnitch).

Remediation

  • Apply vendor patch immediately. Install the OEM security update containing patch ID MOLY01738310. Check your device's February 2026 security patch level string: 2026-02-01 or later indicates coverage.
  • Verify patch level programmatically: adb shell getprop ro.build.version.security_patch — should return 2026-02-01 or newer.
  • Enterprise MDM policy: Enforce minimum security patch level via MDM (e.g., Android Enterprise minimumSecurityPatchLevel policy key).
  • RF environment awareness: In sensitive deployments, use commercial IMSI-catcher detection hardware or applications that alert on anomalous cell parameters.
  • No software-only workaround exists for unpatched devices. The vulnerable code runs in the modem firmware below the Android security model — SELinux, seccomp, and app-layer controls are all irrelevant to this attack path.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →