Dongoing created an issue (kamailio/kamailio#4816)

### Description

Several Kamailio IMS modules read a 4-byte Unsigned32 value out of a Diameter 
**answer** AVP with
`get_4bytes(avp->data.s)` (`cdp/diameter.h:64`) after only a NULL check — they 
do not check
`avp->data.len >= 4`. `get_4bytes` reads four bytes unconditionally, so an AVP 
whose value is
shorter than 4 bytes is read past its declared length.

The value comes off the wire from a Diameter peer (the HSS on Cx, the OCS on 
Ro/Gy, the PCRF on
Rx), which controls the AVP's length. In the common case the extra bytes read 
are the AVP's
4-byte alignment padding (Diameter pads every AVP value, and CDP sizes the 
message buffer with
that padding), so the module computes a **wrong integer value** rather than 
faulting — a
robustness/correctness defect. If a peer sends the short AVP unpadded at the 
very end of the
message the read goes past the message buffer, but even then it is a 1-3 byte 
read that does not
reliably fault, and it is not observable in a running node because the received 
message lives in
the shared-memory pool (`receiver.c`: `sp->msg = shm_malloc(...)`, and 
`msg->buf.s` points into
it), where such a read is caught by neither AddressSanitizer nor 
`DBG_SR_MEMORY`. The point is
simply that these sites read four bytes from a field that may be shorter, 
without checking — the
fix is to add the length check.

This is systemic — the same unchecked `get_4bytes(...->data.s)` appears across 
five modules and
all four IMS Diameter interfaces:

| Module | Interface / answer | Sites (Unsigned32 AVP) |
|---|---|---|
| `ims_icscf/cxdx_avp.c` | Cx UAA/LIA | `:326` Experimental-Result-Code, 
`:400`/`:402` Mandatory/Optional-Capability |
| `ims_auth/cxdx_avp.c` | Cx MAA | `:337` Experimental-Result-Code |
| `ims_registrar_scscf/cxdx_avp.c` | Cx SAA | `:333` Experimental-Result-Code |
| `ims_charging/ccr.c` | Ro/Gy CCA | `:368`/`:372` CC-Request-Type/Number, 
`:392` CC-Time, `:404` Validity-Time, `:408` Time-Quota-Threshold, `:411` 
Result-Code, `:420` Final-Unit-Action, `:483` |
| `ims_qos/rx_avp.c` | Rx AAA | `:1329`, `:1353` Result-Code, `:1361` 
Experimental-Result-Code |

Representative (from `ims_icscf/cxdx_avp.c`, identical shape in the others):

```c
avp = ...FindMatchingAVP(..., AVP_IMS_Experimental_Result_Code, ...);
if(!avp || !avp->data.s) { ... return 0; }        /* NULL check only, no length 
check */
*data = get_4bytes(avp->data.s);                   /* reads data.s[0..3] */
```

`get_4bytes(_b)` expands to `_b[0]<<24 | _b[1]<<16 | _b[2]<<8 | _b[3]`.

That this is an oversight is shown by the sibling readers in the same files 
that **do** check the
length first:

```c
/* ims_icscf/cxdx_avp.c:556, ims_auth:585, ims_registrar_scscf:581 -- 
SIP-Item-Number */
if(!avp || avp->data.len != 4)
    return 0;
*item_number = get_4bytes(avp->data.s);
```

and by CDP's own getter `AAAGetUnsigned32` 
(`cdp_avp/avp_get_base_data_format.c:92`), which
rejects `data.len < 4`. The listed sites bypass both. (CDP's wire parser also 
uses `get_4bytes`,
but there each read is bounds-checked against the message buffer first — those 
are fine.)

There is no XSD or module option in these paths; the modules parse these answer 
AVPs on every
Cx/Ro/Gy/Rx transaction, so it applies in the default configuration.

Expected: an Unsigned32 AVP shorter than 4 bytes should be rejected. Actual: 
`get_4bytes` reads
4 bytes regardless of `avp->data.len`.

### Troubleshooting

#### Reproduction

1. Peer a Kamailio IMS node with the relevant Diameter peer (I-CSCF/S-CSCF ↔ 
HSS on Cx, a
   charging-enabled node ↔ OCS on Ro/Gy, or a P-CSCF ↔ PCRF on Rx).
2. Cause the corresponding transaction (register a subscriber → UAR/UAA, 
MAR/MAA, SAR/SAA; a
   charged session → CCR/CCA; a media session → AAR/AAA).
3. Answer with a message whose `Experimental-Result-Code` / `Result-Code` / a 
`Capability` / a
   CCA quota unit (`CC-Time`, `Validity-Time`, …) AVP carries fewer than 4 
bytes of value. The
   module reads 4 bytes, i.e. past the AVP's declared value length.

Without a full setup, the read past the value is shown by compiling the 
`get_4bytes` +
check-then-read pattern verbatim under AddressSanitizer and feeding it a 1-byte 
AVP
(`repro_icscf_get4bytes.c`, `gcc -fsanitize=address`).

#### Debugging Data

AddressSanitizer on the standalone reproducer (verbatim `get_4bytes` macro + 
the check-then-read
from `cxdx_avp.c`), with a 1-byte AVP value:

```
==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1 at 0x... thread T0
    #0 read_u32_avp  repro_icscf_get4bytes.c:39   [== get_4bytes(avp->data.s): 
cxdx_avp.c:326 / ccr.c:411 / rx_avp.c:1353 ...]
    #1 main          repro_icscf_get4bytes.c:57
0x... is located 0 bytes to the right of 1-byte region  (the AVP value < 4 
bytes)
SUMMARY: AddressSanitizer: heap-buffer-overflow
```

(Full log: `evidence/asan-bug6-icscf-oob-read.log`. The mechanism is identical 
at every site
listed above. This isolated harness — where the value is a plain `malloc()` 
allocation — is the
evidence for the read; in a running node the received message is in the 
shared-memory pool, so
the over-read is not observable by ASAN or `DBG_SR_MEMORY`, as noted in the 
Description.)

#### Log Messages

```
(None — the read is silent; it does not fault on a normal allocator.)
```

#### SIP Traffic

Not SIP — the trigger is a Diameter answer (UAA/LIA/MAA/SAA on Cx, CCA on 
Ro/Gy, AAA on Rx)
carrying an Unsigned32 AVP with fewer than 4 bytes of value, e.g. an 
`Experimental-Result`(297)
grouped AVP whose `Experimental-Result-Code`(298) child holds 3 bytes.

### Possible Solutions

Check the length before every `get_4bytes(avp->data.s)` on wire data (or route 
them through
`AAAGetUnsigned32`), mirroring the existing checked readers:

```c
if(!avp || !avp->data.s || avp->data.len < 4)
    return 0;                       /* or skip this AVP */
*data = get_4bytes(avp->data.s);
```

Apply at all sites in the table above (`ims_icscf`, `ims_auth`, 
`ims_registrar_scscf`,
`ims_charging`, `ims_qos`).

### Additional Information

- [x] LLM/AI Assistants were involved in discovery, analysis or submission of 
the issue
- [ ] It happened in a production deployment
- [x] It happened in a testing or development deployment
- [ ] Testing or fuzzing tools were used to generate SIP traffic when it 
happened

  * **Kamailio Version** - output of `kamailio -v`

```
version: kamailio 5.5.0 (x86_64/linux) d730fa-dirty
compiled with gcc 11.4.0
```

(Code paths unchanged on current `master`.)

* **Operating System**:

```
Ubuntu 22.04 (jammy), x86_64, Linux — Docker container

[bug1-scscf-crash.log](https://github.com/user-attachments/files/29854305/bug1-scscf-crash.log)

-- 
Reply to this email directly or view it on GitHub:
https://github.com/kamailio/kamailio/issues/4816
You are receiving this because you are subscribed to this thread.

Message ID: <kamailio/kamailio/issues/[email protected]>
_______________________________________________
Kamailio - Development Mailing List -- [email protected]
To unsubscribe send an email to [email protected]
Important: keep the mailing list in the recipients, do not reply only to the 
sender!

Reply via email to