From e9932bde9d39b3441f836560d9cbab87280590d9 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Thu, 2 Jul 2026 21:02:31 +0000 Subject: [PATCH 1/2] libqcdm: fix heap out-of-bounds read in qcdm_cmd_pilot_sets_result Validate the active, candidate, and neighbor count fields in qcdm_cmd_pilot_sets_result() before parsing the sets. Ensure that the sum of counts does not exceed the size of the static sets array in the response structure, and that the calculated size based on the counts fits within the received buffer len. https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/work_items/1069 Signed-off-by: Aleksander Morgado --- libqcdm/src/commands.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libqcdm/src/commands.c b/libqcdm/src/commands.c index b78c26b7..f182016d 100644 --- a/libqcdm/src/commands.c +++ b/libqcdm/src/commands.c @@ -672,12 +672,24 @@ qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error) QcdmResult *result = NULL; DMCmdPilotSetsRsp *rsp = (DMCmdPilotSetsRsp *) buf; size_t sets_len; + size_t total_count; qcdm_return_val_if_fail (buf != NULL, NULL); if (!check_command (buf, len, DIAG_CMD_PILOT_SETS, sizeof (DMCmdPilotSetsRsp), out_error)) return NULL; + /* Validate that the modem-supplied counts fit in the fixed sets[] array + * and in the actually-received response. */ + total_count = (size_t) rsp->active_count + rsp->candidate_count + rsp->neighbor_count; + if (total_count > (sizeof (rsp->sets) / sizeof (rsp->sets[0])) || + ((sizeof (*rsp) - sizeof (rsp->sets)) + total_count * sizeof (DMCmdPilotSetsSet)) > len) { + qcdm_err (0, "DM Pilot Sets response counts out of range"); + if (out_error) + *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH; + return NULL; + } + result = qcdm_result_new (); sets_len = rsp->active_count * sizeof (DMCmdPilotSetsSet); From 96cb590724db398505bd585b4fc88738676188a2 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Thu, 2 Jul 2026 21:02:46 +0000 Subject: [PATCH 2/2] libqcdm: fix heap out-of-bounds read in qcdm_log_item_evdo_pilot_sets_v2_new Validate the active, candidate, and remaining count fields in qcdm_log_item_evdo_pilot_sets_v2_new() before parsing the sets. Ensure the received buffer is large enough to contain the number of sets claimed by the counts. https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/work_items/1070 Signed-off-by: Aleksander Morgado --- libqcdm/src/logs.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libqcdm/src/logs.c b/libqcdm/src/logs.c index 91152c42..090e0a4f 100644 --- a/libqcdm/src/logs.c +++ b/libqcdm/src/logs.c @@ -88,6 +88,7 @@ qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len, int *out_erro QcdmResult *result = NULL; DMLogItemEvdoPilotSetsV2 *pilot_sets; DMCmdLog *log_cmd = (DMCmdLog *) buf; + size_t avail; size_t sets_len; qcdm_return_val_if_fail (buf != NULL, NULL); @@ -97,6 +98,21 @@ qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len, int *out_erro pilot_sets = (DMLogItemEvdoPilotSetsV2 *) log_cmd->data; + /* check_log_item() guarantees len >= sizeof (DMCmdLog) + sizeof (DMLogItemEvdoPilotSetsV2), + * so 'avail' cannot underflow. */ + /* Ensure the flexible sets[] array actually contains as many entries as + * the header claims; the modem fully controls these counts. */ + avail = len - sizeof (DMCmdLog) - sizeof (DMLogItemEvdoPilotSetsV2); + sets_len = ((size_t) pilot_sets->active_count + + (size_t) pilot_sets->candidate_count + + (size_t) pilot_sets->remaining_count) + * sizeof (DMLogItemEvdoPilotSetsV2Pilot); + if (sets_len > avail) { + if (out_error) + *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH; + return NULL; + } + result = qcdm_result_new (); sets_len = pilot_sets->active_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot);