home intel cve-2026-42799-asr-kestrel-nr-fw-oob-read
CVE Analysis 2026-04-30 · 8 min read

CVE-2026-42799: OOB Read in ASR Kestrel nr_fw Power Control

Out-of-bounds read in ASR Kestrel's NrPwrCtrl.C allows buffer overflow via unchecked array indexing in 5G NR power control firmware. Affects all Kestrel builds before 2026-02-10.

#out-of-bounds-read#buffer-overflow#memory-corruption#power-control#cross-platform
Technical mode — for security professionals
▶ Attack flow — CVE-2026-42799 · Memory Corruption
ATTACKERRemote / unauthMEMORY CORRUPTIOCVE-2026-42799Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-42799 is a CVSS 7.4 HIGH out-of-bounds read vulnerability in the nr_fw module of ASR's Kestrel baseband firmware. The flaw originates in Code/Nr/nr_fw/RA/src/NrPwrCtrl.C, the 5G NR uplink power control subsystem responsible for computing UE transmit power across component carriers. An attacker with influence over radio resource control signaling — either via a rogue base station or crafted RRC messages — can drive the vulnerable read path, resulting in out-of-bounds memory access that corrupts adjacent firmware state.

The vulnerability was assigned by ASR Micro's PSIRT and patched in Kestrel builds dated 2026-02-10 and later. No in-the-wild exploitation has been reported, but the attack surface is reachable from the radio layer without physical access, elevating practical risk in cellular environments.

Root cause: NrPwrCtrl_ComputeTxPower() indexes a fixed-size carrier configuration array using a carrierIdx value derived from RRC signaling without validating it against NR_MAX_CC, allowing reads and subsequent writes up to (carrierIdx - NR_MAX_CC) * sizeof(NrCcPwrCfg_t) bytes past the array boundary.

Affected Component

The nr_fw module is a firmware-layer 5G NR stack component running on ASR's Kestrel modem DSP cores. It is distinct from the AP-side stack and executes in a privileged firmware execution environment. The power control subsystem (RA/src/NrPwrCtrl.C) manages per-cell uplink power headroom and max output power configuration for multi-carrier NR operation.

Affected product families per ASR's PSIRT disclosure include Kestrel-based 5G chipsets — specifically the ASR1901, ASR1903, and ASR8603 cellular baseband lines. All firmware builds prior to the 2026-02-10 patch date are vulnerable.

Root Cause Analysis

The vulnerable function NrPwrCtrl_ComputeTxPower() iterates over configured component carriers to compute uplink transmit power. A carrierIdx value sourced from the NR RRC configuration structure is used directly to subscript a statically-allocated array of carrier power configurations. No upper-bound validation occurs before the access.

/* Code/Nr/nr_fw/RA/src/NrPwrCtrl.C */

#define NR_MAX_CC       4
#define NR_MAX_PUSCH_PW 23  /* dBm */

typedef struct {
    int16_t  p0NominalPusch;    /* P0-PUSCH nominal power */
    int16_t  p0UePusch;         /* UE-specific P0 offset  */
    uint8_t  alphaIndex;        /* pathloss compensation coefficient index */
    uint8_t  closedLoopIdx;     /* closed-loop power control index */
    int16_t  deltaTF;           /* transport format delta */
    uint32_t flags;             /* enabled / configured bitmask */
} NrCcPwrCfg_t;

/* Statically allocated in .bss, size = NR_MAX_CC * sizeof(NrCcPwrCfg_t) = 4 * 12 = 48 bytes */
static NrCcPwrCfg_t gNrCcPwrCfg[NR_MAX_CC];

int32_t NrPwrCtrl_ComputeTxPower(NrPwrCtrlCtx_t *ctx,
                                  uint8_t         carrierIdx,
                                  int16_t         plEst_dB,
                                  int16_t        *txPwr_out)
{
    NrCcPwrCfg_t *cfg;
    int32_t       p0, alpha, txPwr;

    /* BUG: carrierIdx is derived from RRC CellGroupConfig->physicalCellGroupConfig
     * and is never validated against NR_MAX_CC before use as array index.
     * Attacker-controlled values 0x04..0xFF index past gNrCcPwrCfg boundary. */
    cfg = &gNrCcPwrCfg[carrierIdx];   // BUG: missing bounds check here

    /* Read p0 fields from out-of-bounds memory */
    p0    = (int32_t)cfg->p0NominalPusch + (int32_t)cfg->p0UePusch;
    alpha = Nr_GetAlphaFromIndex(cfg->alphaIndex);  /* further OOB read propagation */

    txPwr = p0 + alpha * plEst_dB + cfg->deltaTF + ctx->f_c;

    if (txPwr > NR_MAX_PUSCH_PW)
        txPwr = NR_MAX_PUSCH_PW;

    *txPwr_out = (int16_t)txPwr;

    /* BUG: Result written back to per-CC log structure also indexed by carrierIdx */
    ctx->ccLog[carrierIdx].lastTxPwr = (int16_t)txPwr;  // BUG: second OOB write here

    return NR_PWR_CTRL_OK;
}

The alphaIndex field read from out-of-bounds memory is subsequently passed to Nr_GetAlphaFromIndex(), which itself indexes a lookup table — creating a second-order OOB read. The final write through ctx->ccLog[carrierIdx] converts the read into a limited write primitive if ctx->ccLog is a heap-resident array.

Memory Layout

The static BSS layout in Kestrel firmware places gNrCcPwrCfg adjacent to other power control state. With sizeof(NrCcPwrCfg_t) == 12 bytes and NR_MAX_CC == 4, valid indices are 0–3. Index 4 begins immediately past the array boundary.

struct NrCcPwrCfg_t {
    /* +0x00 */ int16_t  p0NominalPusch;   // P0 nominal PUSCH power, dBm
    /* +0x02 */ int16_t  p0UePusch;        // UE-specific offset
    /* +0x04 */ uint8_t  alphaIndex;       // index into Nr_AlphaTable[8]
    /* +0x05 */ uint8_t  closedLoopIdx;    // 0 or 1
    /* +0x06 */ int16_t  deltaTF;          // TF correction factor
    /* +0x08 */ uint32_t flags;            // enabled/configured bitmask
    // total: 12 (0x0C) bytes
};
BSS SEGMENT — NR POWER CONTROL STATIC STATE:

Offset   Size   Symbol
──────────────────────────────────────────────────────────────────
+0x0000  0x030  gNrCcPwrCfg[0..3]   ← valid array (48 bytes)
                  [0] @ +0x000  p0Nom=0xFFF4 p0UE=0x0000 alpha=3
                  [1] @ +0x00C  p0Nom=0xFFF4 p0UE=0x0002 alpha=3
                  [2] @ +0x018  p0Nom=0xFFF0 p0UE=0x0000 alpha=5
                  [3] @ +0x024  p0Nom=0xFFEC p0UE=0x0000 alpha=7
+0x0030  0x00C  gNrCcPwrCfg[4]      ← OOB: begins here (carrierIdx=4)
                  ACTUAL DATA: gNrTimingAdvCtx fields (adjacent struct)
                  bytes: [TA_ref_lo][TA_ref_hi][state][reserved]...
+0x003C  0x00C  gNrCcPwrCfg[5]      ← OOB: gNrTimingAdvCtx+0x0C
...
+0x00C0  0x00C  gNrCcPwrCfg[16]     ← OOB: reaches NrSchedCtx base

AFTER CORRUPTION (carrierIdx=0xFF, write path via ctx->ccLog):
[ gNrCcPwrCfg[0..3] intact @ BSS+0x000 ]
[ gNrTimingAdvCtx CORRUPTED @ BSS+0x030 ]
  TA_state overwritten with attacker-derived txPwr value
  Timing advance feedback loop destabilized → modem assertion or
  memory corruption escalation via TA update path

Exploitation Mechanics

EXPLOIT CHAIN (rogue base station / compromised RRC path):

1. Stand up rogue NR cell (srsRAN or similar) broadcasting on target UE ARFCN.
   Force UE to camp via stronger RSRP / spoofed SIB1 MCC/MNC.

2. Complete RRC Setup. In RRCReconfiguration, include CellGroupConfig with
   secondaryCellGroup containing >= 5 SCells (pushing cellIndex to 0x04+).
   Alternatively: craft SpCellConfig->uplinkConfig->carrierIdx directly
   in vendor extension IEs if ASR stack honors them pre-authentication.

3. Trigger NrPwrCtrl_ComputeTxPower() with carrierIdx=0x04 via PUSCH
   scheduling on the malformed SCell. The UE scheduler invokes power
   control for each configured CC before first transmission.

4. cfg = &gNrCcPwrCfg[4] resolves to gNrTimingAdvCtx base address.
   p0NominalPusch is read from TA ctx bytes [0:1] — attacker can influence
   these by manipulating prior TA commands (RAR TA, MAC CE TA update).

5. alphaIndex read from OOB byte [4] indexes Nr_AlphaTable[].
   If attacker sets this byte > 7 (table has 8 entries), second OOB read
   occurs in Nr_GetAlphaFromIndex(), reading arbitrary firmware memory
   as a Q8 fixed-point alpha coefficient.

6. txPwr computed from attacker-influenced values. Written back via
   ctx->ccLog[carrierIdx].lastTxPwr — if ctx->ccLog is heap-allocated,
   this is an OOB write ~(carrierIdx * 2) bytes past ccLog boundary.

7. Corrupted ccLog region overlaps with NrSchedCtx function pointer table
   in favorable heap layouts. Scheduler invocation → control flow hijack.

IMPACT: Firmware crash (reliable DoS) at minimum. Control flow corruption
        in specific heap layouts escalates to arbitrary code execution in
        the baseband DSP trust domain.

Patch Analysis

ASR's fix in the 2026-02-10 Kestrel build introduces explicit bounds validation on carrierIdx at both the primary access site and the write-back path. The patch also adds a second guard in the caller (NrPwrCtrl_UpdateCcConfig()) to reject out-of-range indices at the RRC configuration ingestion point, preventing the bad index from propagating into the runtime power control loop at all.

// BEFORE (vulnerable — Code/Nr/nr_fw/RA/src/NrPwrCtrl.C pre-2026-02-10):
int32_t NrPwrCtrl_ComputeTxPower(NrPwrCtrlCtx_t *ctx,
                                  uint8_t         carrierIdx,
                                  int16_t         plEst_dB,
                                  int16_t        *txPwr_out)
{
    NrCcPwrCfg_t *cfg = &gNrCcPwrCfg[carrierIdx];  // no validation
    int32_t p0 = (int32_t)cfg->p0NominalPusch + (int32_t)cfg->p0UePusch;
    int32_t alpha = Nr_GetAlphaFromIndex(cfg->alphaIndex);
    int32_t txPwr = p0 + alpha * plEst_dB + cfg->deltaTF + ctx->f_c;
    if (txPwr > NR_MAX_PUSCH_PW) txPwr = NR_MAX_PUSCH_PW;
    *txPwr_out = (int16_t)txPwr;
    ctx->ccLog[carrierIdx].lastTxPwr = (int16_t)txPwr;  // no validation
    return NR_PWR_CTRL_OK;
}

// AFTER (patched — Kestrel >= 2026-02-10):
int32_t NrPwrCtrl_ComputeTxPower(NrPwrCtrlCtx_t *ctx,
                                  uint8_t         carrierIdx,
                                  int16_t         plEst_dB,
                                  int16_t        *txPwr_out)
{
    NrCcPwrCfg_t *cfg;
    int32_t       p0, alpha, txPwr;

    /* PATCH: reject invalid carrier index before any array access */
    if (carrierIdx >= NR_MAX_CC) {
        NR_LOG_ERR("PwrCtrl: invalid carrierIdx %u (max %u)",
                   carrierIdx, NR_MAX_CC);
        return NR_PWR_CTRL_ERR_INVALID_CC;
    }

    cfg   = &gNrCcPwrCfg[carrierIdx];   // safe: index validated above
    p0    = (int32_t)cfg->p0NominalPusch + (int32_t)cfg->p0UePusch;

    /* PATCH: clamp alphaIndex before table lookup */
    uint8_t safeAlphaIdx = cfg->alphaIndex & 0x07u;  // Nr_AlphaTable has 8 entries
    alpha = Nr_GetAlphaFromIndex(safeAlphaIdx);

    txPwr = p0 + alpha * plEst_dB + cfg->deltaTF + ctx->f_c;
    if (txPwr > NR_MAX_PUSCH_PW) txPwr = NR_MAX_PUSCH_PW;

    *txPwr_out = (int16_t)txPwr;

    /* PATCH: guard write-back path with same index check */
    if (carrierIdx < ctx->ccLogCount) {
        ctx->ccLog[carrierIdx].lastTxPwr = (int16_t)txPwr;
    }

    return NR_PWR_CTRL_OK;
}

/* ADDITIONAL PATCH in caller — NrPwrCtrl_UpdateCcConfig(): */
int32_t NrPwrCtrl_UpdateCcConfig(uint8_t carrierIdx, NrCcPwrCfg_t *newCfg)
{
    /* PATCH: validate at RRC config ingestion, not just runtime */
    if (carrierIdx >= NR_MAX_CC || newCfg == NULL)
        return NR_PWR_CTRL_ERR_INVALID_CC;

    memcpy(&gNrCcPwrCfg[carrierIdx], newCfg, sizeof(NrCcPwrCfg_t));
    return NR_PWR_CTRL_OK;
}

The defense-in-depth approach — validating at ingestion and at use — is correct. A single check at the RRC layer is insufficient because carrierIdx can arrive through multiple scheduler and MAC paths that bypass RRC config update functions.

Detection and Indicators

Detecting active exploitation requires baseband-layer visibility. On affected devices:

  • Modem crash logs: Look for NR_PWR_CTRL_ERR assertions or DSP exception dumps with PC values inside NrPwrCtrl_ComputeTxPower or Nr_GetAlphaFromIndex. Kestrel firmware logs to a circular buffer readable via Qualcomm-compatible DIAG protocol on ASR debug builds.
  • RRC message anomalies: RRCReconfiguration messages specifying more than 4 SCells or carrying malformed uplinkConfig IEs for non-existent carriers. Monitor with LTE/NR air interface analyzers (e.g., SCAT, QC DIAG).
  • UE behavior: Unexpected modem resets (/dev/log/radio on Android showing ril-daemon restart), sustained UE attachment failures on legitimate cells following a rogue cell encounter.
DIAG LOG SIGNATURE (pre-crash, debug firmware):
[NR_FW][PwrCtrl] ComputeTxPower: carrierIdx=0x06 p0=0x????
[NR_FW][PwrCtrl] alphaIndex=0xC3 (INVALID, clamped/unclamped depending on build)
[NR_FW][ASSERT]  nr_fw/RA/src/NrPwrCtrl.C:148 — access violation
DSP ABORT: PC=0x???????? LR=0x???????? carrierIdx=6 MAX=4

Remediation

  • Update firmware to ASR Kestrel build dated 2026-02-10 or later. This is the only complete fix. Contact your ASR representative or OEM for the applicable modem firmware package for ASR1901/ASR1903/ASR8603 platforms.
  • OEM device vendors shipping products with Kestrel-family modems must integrate the updated nr_fw binary and release an OTA update. ASR's PSIRT advisory is available at https://www.asrmicro.com/en/goods/psirt?cid=44.
  • No userspace workaround exists. The vulnerable code runs in the modem DSP firmware below the Android/Linux radio interface layer. Disabling 5G NR (Settings → Network → Preferred network type → LTE only) prevents reaching the NR power control path and eliminates the attack surface until patching is possible.
  • Network operators can mitigate rogue BTS vectors by deploying IMSI catcher detection and enforcing NR cell authentication where supported by their RAN equipment.
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 →