[PATCH] net: ipa: validate QMI sender for modem-only server requests

From: Kenneth Kabogo

Date: Thu Sep 10 2026 - 14:18:00 EST


The IPA driver's QMI server has two request handlers,
ipa_server_indication_register() and ipa_server_driver_init_complete(),
that are only ever legitimately sent by the paired modem. Neither checks
the sender address (struct sockaddr_qrtr *sq) against ipa_qmi->modem_sq,
which the driver already caches when the modem's QMI service appears in
ipa_client_new_server().

Both handlers set a readiness flag (indication_requested, uc_ready) and
call ipa_qmi_ready(), which starts the modem netdev via ipa_modem_start()
-> register_netdev() once both flags are set. A local process able to
send QMI messages on the qrtr socket, other than the modem, can spoof
both signals and drive ipa_qmi_ready() to completion before the modem
has confirmed its endpoint configuration, bringing up the modem network
interface out of sequence with real modem readiness. The realistic
outcome is a data-path stall requiring a subsystem restart to recover.
It is not a memory-safety issue.

On Android the qrtr socket is not reachable by untrusted apps (SELinux
neverallow on qipcrtr_socket), so this is gated to privileged system
components, hence the low severity. It is still a missing trust check on
a cross-processor control interface.

Reject server requests whose sender does not match the cached modem
address. modem_sq is populated before the modem sends these requests and
is zeroed in ipa_server_bye(); a zeroed modem_sq does not match any real
sender's address, so requests arriving during the teardown window are
rejected without a separate check.

Found by code inspection; no runtime proof-of-concept.

Signed-off-by: Kenneth Kabogo <kennethkabogo2@xxxxxxxxx>
---
Build-tested only (arm64 allmodconfig); I don't have IPA hardware to
test at runtime.

This assumes modem_sq is always populated (via ipa_client_new_server(),
the NEW_SERVER path) before the modem sends INDICATION_REGISTER /
DRIVER_INIT_COMPLETE. That ordering looks right from the code, but QMI
delivery is asynchronous - if the modem's request can legitimately
arrive before we've processed its NEW_SERVER event, this would wrongly
drop it, and the check should instead only be enforced when modem_sq is
non-zero. Happy to respin that way if you prefer.

drivers/net/ipa/ipa_qmi.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/drivers/net/ipa/ipa_qmi.c b/drivers/net/ipa/ipa_qmi.c
index d771f3a71..a7cc98b27 100644
--- a/drivers/net/ipa/ipa_qmi.c
+++ b/drivers/net/ipa/ipa_qmi.c
@@ -168,6 +168,14 @@ static const struct qmi_ops ipa_server_ops = {
.bye = ipa_server_bye,
};

+/* True if a QMI request arrived from the modem we are paired with */
+static bool ipa_server_from_modem(const struct ipa_qmi *ipa_qmi,
+ const struct sockaddr_qrtr *sq)
+{
+ return sq->sq_node == ipa_qmi->modem_sq.sq_node &&
+ sq->sq_port == ipa_qmi->modem_sq.sq_port;
+}
+
/* Callback function to handle an INDICATION_REGISTER request message from the
* modem. This informs the AP that the modem is now ready to receive the
* INIT_COMPLETE indication message.
@@ -185,6 +193,13 @@ static void ipa_server_indication_register(struct qmi_handle *qmi,
ipa_qmi = container_of(qmi, struct ipa_qmi, server_handle);
ipa = container_of(ipa_qmi, struct ipa, qmi);

+ if (!ipa_server_from_modem(ipa_qmi, sq)) {
+ dev_warn_ratelimited(ipa->dev,
+ "ignoring QMI request from non-modem sender %u:%u\n",
+ sq->sq_node, sq->sq_port);
+ return;
+ }
+
rsp.rsp.result = QMI_RESULT_SUCCESS_V01;
rsp.rsp.error = QMI_ERR_NONE_V01;

@@ -214,6 +229,13 @@ static void ipa_server_driver_init_complete(struct qmi_handle *qmi,
ipa_qmi = container_of(qmi, struct ipa_qmi, server_handle);
ipa = container_of(ipa_qmi, struct ipa, qmi);

+ if (!ipa_server_from_modem(ipa_qmi, sq)) {
+ dev_warn_ratelimited(ipa->dev,
+ "ignoring QMI request from non-modem sender %u:%u\n",
+ sq->sq_node, sq->sq_port);
+ return;
+ }
+
rsp.rsp.result = QMI_RESULT_SUCCESS_V01;
rsp.rsp.error = QMI_ERR_NONE_V01;

--
2.50.1 (Apple Git-155)