proto: Prevent SMF crash on malformed PCO/EPCO during parsing

ogs_pco_parse() previously relied on ogs_assert() to verify the bounds
of Protocol/Container fields while parsing PCO/EPCO data. If the outer
PCO/EPCO length was inconsistent with the internal container encoding
(e.g., truncated Container-ID, Container-Length, or container data),
the assert would trigger and terminate the process.

Because PCO/EPCO is derived from UE-supplied NAS messages (e.g.,
PDU Session Establishment Request), a malformed EPCO IE could trigger
a remote SMF crash, resulting in a denial-of-service condition.

This patch replaces the assert-based bounds checks with explicit
runtime validation and returns an error when malformed or truncated
PCO/EPCO is detected. The SMF can then reject the request cleanly
instead of aborting.

Checks added:
- Validate minimum PCO/EPCO length before accessing header fields
- Verify Container-ID bounds
- Verify Container-Length bounds
- Verify container payload length
- Detect container count overflow beyond
  OGS_MAX_NUM_OF_PROTOCOL_OR_CONTAINER_ID

With these changes, malformed EPCO inputs are safely rejected and the
SMF remains operational.

Issues: #4341
This commit is contained in:
Sukchan Lee 2026-03-10 22:35:42 +09:00
parent 42506202e8
commit 6a29f11115

View file

@ -447,26 +447,55 @@ int ogs_pco_parse(ogs_pco_t *pco, unsigned char *data, int data_len)
memset(pco, 0, sizeof(ogs_pco_t));
if (data_len < 1) {
ogs_error("PCO/EPCO too short [%d]", data_len);
return -EINVAL;
}
pco->ext = source->ext;
pco->configuration_protocol = source->configuration_protocol;
size++;
while(size < data_len && i < OGS_MAX_NUM_OF_PROTOCOL_OR_CONTAINER_ID) {
ogs_pco_id_t *id = &pco->ids[i];
ogs_assert(size + sizeof(id->id) <= data_len);
if (size + (int)sizeof(id->id) > data_len) {
ogs_error("PCO/EPCO truncated before Container-ID "
"[offset:%d len:%d]", size, data_len);
return -EINVAL;
}
memcpy(&id->id, data + size, sizeof(id->id));
id->id = be16toh(id->id);
size += sizeof(id->id);
ogs_assert(size + sizeof(id->len) <= data_len);
if (size + (int)sizeof(id->len) > data_len) {
ogs_error("PCO/EPCO truncated before Container-Length "
"[id:0x%x offset:%d len:%d]",
id->id, size, data_len);
return -EINVAL;
}
memcpy(&id->len, data + size, sizeof(id->len));
size += sizeof(id->len);
if (size + id->len > data_len) {
ogs_error("PCO/EPCO truncated Container data "
"[id:0x%x container-len:%u offset:%d len:%d]",
id->id, id->len, size, data_len);
return -EINVAL;
}
id->data = data + size;
size += id->len;
i++;
}
if (size < data_len) {
ogs_error("PCO/EPCO exceeds maximum number of containers "
"[%d]", OGS_MAX_NUM_OF_PROTOCOL_OR_CONTAINER_ID);
return -EINVAL;
}
pco->num_of_id = i;
ogs_expect(size == data_len);