From: netdev-bot+sashiko@kernel.org
To: kennethkabogo2@gmail.com
Cc: elder@kernel.org, netdev@vger.kernel.org, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] net: ipa: validate QMI sender for modem-only server requests
Date: Fri, 11 Sep 2026 18:45:34 +0000 [thread overview]
Message-ID: <178915233401.219967.3094305844336909787@kernel.org> (raw)
In-Reply-To: <20260910181737.55570-1-kennethkabogo2@gmail.com>
Thank you for your contribution! Sashiko AI review found 7 potential
issue(s) to consider:
Critical: 0 · High: 3 · Medium: 4 · Low: 0
- [High] The patch guards the two readiness flags set from the server
handle (indication_requested at ipa_qmi.c:210, uc_ready at :246) but…
- [High] The new sender check anchors trust in ipa_qmi->modem_sq, but
modem_sq is itself learned from an unauthenticated QRTR name-service…
- [High] The new gate rejects every request whenever modem_sq is zero or
stale, and two structurally reachable paths put it there while the…
- [Medium] ipa_server_from_modem() (drivers/net/ipa/ipa_qmi.c:172-177)
requires sq->sq_port == ipa_qmi->modem_sq.sq_port, but modem_sq is the…
- [Medium] The commit message states that a local non-modem process "can
spoof both signals and drive ipa_qmi_ready() to completion before the…
- [Medium] The new gate ipa_server_from_modem() (ipa_qmi.c:172-177) reads
ipa_qmi->modem_sq.sq_node and .sq_port from the *server* QMI handle's…
- [Medium] The commit message relies on ipa_server_bye() zeroing modem_sq
to make teardown-window requests fail the new check, but…
--
net: ipa: validate QMI sender for modem-only server requests
This adds a helper ipa_server_from_modem() and uses it in
ipa_server_indication_register() and ipa_server_driver_init_complete()
to drop QMI requests whose sender does not match the cached
ipa_qmi->modem_sq. Requests from any other sender are logged and
discarded.
A few questions on the commit message and the new check below.
The commit message says:
"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"
[Severity: Medium]
Is that mechanism description accurate for the two handlers being changed?
ipa_qmi_ready() has a third gate that neither patched handler touches:
drivers/net/ipa/ipa_qmi.c:ipa_qmi_ready() {
/* We aren't ready until the modem and microcontroller are */
if (!ipa_qmi->modem_ready || !ipa_qmi->uc_ready)
return;
...
}
and modem_ready is only set after the AP's own INIT_DRIVER transaction
completes successfully in ipa_client_init_driver_work():
if (!ret) {
ipa_qmi->modem_ready = true;
ipa_qmi_ready(ipa_qmi); /* We might be ready now */
So spoofing only INDICATION_REGISTER and DRIVER_INIT_COMPLETE cannot reach
ipa_modem_start() "before the modem has confirmed its endpoint
configuration" — modem_ready is that confirmation. The effect appears to
be bring-up after INIT_DRIVER succeeds but before the genuine
DRIVER_INIT_COMPLETE, unless the sender also supplies the client-side
response or supplants the cached service.
Could the message be reworded to describe what the two patched handlers can
actually do, and mention the client-handle path?
> diff --git a/drivers/net/ipa/ipa_qmi.c b/drivers/net/ipa/ipa_qmi.c
> index d771f3a71f94f..a7cc98b27e33c 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;
> +}
> +
[Severity: Medium]
Is the sq_port term correct here? modem_sq is written only in
ipa_client_new_server(), from the modem's advertised service address:
ipa_qmi->modem_sq.sq_family = AF_QIPCRTR;
ipa_qmi->modem_sq.sq_node = svc->node;
ipa_qmi->modem_sq.sq_port = svc->port;
That is the port the AP's client handle sends INIT_DRIVER to. The sq
passed to the two patched handlers is the source address of a request that
arrived on the AP's server handle, i.e. the modem's QMI client endpoint.
Is a peer's client port guaranteed to equal its advertised service port?
This driver itself is a counterexample: ipa_qmi_setup() creates two
qmi_handles, each bound to its own qrtr port, so a symmetric modem
implementation would send from a port other than the one its service is
registered on.
If those two ports differ on real firmware, every legitimate request fails
the check, indication_requested and uc_ready are never set, and
ipa_modem_start() is never called. DRIVER_INIT_COMPLETE is one-shot per
initial boot, so there is no retry. Has this been confirmed on hardware?
[Severity: High]
Does anchoring trust in modem_sq close the described hole, given how
modem_sq is learned? ipa_client_new_server() stores whatever node and port
were announced for service 0x31 instance 2, with no check that the
announcing node is the modem's remote qrtr node.
For a local sender, the name service rewrites the announced address to the
sender's own:
net/qrtr/ns.c:ctrl_cmd_new_server() {
/* Ignore specified node and port for local servers */
if (from->sq_node == qrtr_ns.local_node) {
node_id = from->sq_node;
port = from->sq_port;
}
...
}
and binding an ephemeral port needs no capability:
net/qrtr/af_qrtr.c:qrtr_port_assign() {
} else if (*port < QRTR_MIN_EPH_SOCKET && !capable(CAP_NET_ADMIN)) {
rc = -EACCES;
...
}
So the same actor the commit message describes can register a fake IPA
modem service, become the cached modem_sq, receive the AP's INIT_DRIVER
request, answer it, and then send INDICATION_REGISTER and
DRIVER_INIT_COMPLETE from that same port — satisfying
ipa_server_from_modem() exactly.
Would validating the peer's qrtr node against the modem remoteproc or
DT-derived node (or rejecting services announced on the local node) be the
check that actually holds?
[Severity: High]
Is the modem_ready path left unguarded on purpose? modem_ready is set when
the INIT_DRIVER response is matched on the client handle, and the QMI core
matches responses by transaction id alone:
drivers/soc/qcom/qmi_interface.c:qmi_handle_message() {
...
txn = idr_find(&qmi->txns, le16_to_cpu(hdr->txn_id));
...
}
The sender address is never compared against the address the request was
sent to, and ipa_client_init_driver() ignores both sq and the decoded
result:
txn->result = 0; /* IPA_QMI_INIT_DRIVER request was successful */
complete(&txn->completion);
ipa_server_bye() deliberately preserves uc_ready and leaves initial_boot
false across modem restarts, so on any non-initial boot modem_ready is the
only remaining gate and a single forged INIT_DRIVER response reaches
ipa_modem_start() by itself. Does the bug class the commit message
describes remain reachable through this sibling path?
[Severity: Medium]
Can these two loads race with the writer? modem_sq is read here from the
server handle's worker, while ipa_client_new_server() writes sq_family,
sq_node and sq_port as three plain stores from the client handle's worker.
Each handle gets its own workqueue:
drivers/soc/qcom/qmi_interface.c:qmi_handle_init() {
qmi->wq = alloc_ordered_workqueue("qmi_msg_handler", 0);
...
}
There is no lock, no READ_ONCE()/WRITE_ONCE() and no release/acquire
pairing, so a reader on the server worker can see sq_node updated while
sq_port is still zero. The gate then returns false and the request is
dropped with no QMI response sent.
[Severity: Medium]
Related to the same unserialized field: the commit message relies on
"is zeroed in ipa_server_bye(); a zeroed modem_sq does not match any real
sender's address"
but ipa_server_bye() runs on the server handle's worker and
ipa_client_new_server() on the client handle's, with nothing ordering BYE
for the dying modem instance against NEW_SERVER for the new one. If the
server worker is delayed across the restart, its
memset(&ipa_qmi->modem_sq, 0, sizeof(ipa_qmi->modem_sq));
lands after modem_sq was re-populated for the new modem, leaving it
all-zero while a real modem is present. Before this patch that only
mis-addressed the optional INIT_COMPLETE indication; now it rejects every
real modem request for the rest of the boot. Is that inversion prevented
somewhere?
> /* 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;
> + }
> +
[Severity: High]
Can a legitimate modem request be dropped here whenever modem_sq is zero or
stale? Two paths put it in that state while the modem is alive.
First, an unrelated subsystem restart. ipa_server_bye() zeroes modem_sq and
ignores its node argument:
drivers/net/ipa/ipa_qmi.c:ipa_server_bye() {
/* The modem client and server go away at the same time */
memset(&ipa_qmi->modem_sq, 0, sizeof(ipa_qmi->modem_sq));
...
}
while the name service fans a BYE for any dying remote node out to every
local server port:
net/qrtr/ns.c:ctrl_cmd_bye() {
xa_for_each(&local_node->servers, index, srv) {
sq.sq_family = AF_QIPCRTR;
sq.sq_node = srv->node;
sq.sq_port = srv->port;
...
}
and the QMI core passes that node straight through:
drivers/soc/qcom/qmi_interface.c:qmi_recv_bye() {
qmi_recv_del_server(qmi, node, -1);
if (ops->bye)
ops->bye(qmi, node);
}
So an ADSP or CDSP restart zeroes modem_sq with the modem still up, and
nothing repopulates it until the modem itself re-registers.
Second, ordering at boot. ipa_qmi_setup() advertises the AP server with
qmi_add_server() before qmi_add_lookup() for the modem service, and
modem_sq is only populated from the asynchronous NEW_SERVER message
processed on the client handle's separate socket and workqueue. Nothing
ties the modem's INDICATION_REGISTER or DRIVER_INIT_COMPLETE to the AP
having already observed the modem's service registration — this is the
ordering concern raised in the cover note.
In both cases the return happens before qmi_send_response(), so the modem's
transaction is never answered, indication_requested and uc_ready stay
false, and there is no retry or re-arm path back into ipa_qmi_ready().
Would explicitly testing whether modem_sq has been populated, and only then
enforcing the match, avoid this?
Also, should ipa_server_bye() compare its node argument against
modem_sq.sq_node before zeroing, now that the zeroed value denies all
requests?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910181737.55570-1-kennethkabogo2%40gmail.com
next prev parent reply other threads:[~2026-09-11 18:45 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 18:17 [PATCH] net: ipa: validate QMI sender for modem-only server requests Kenneth Kabogo
2026-09-11 18:45 ` netdev-bot+sashiko [this message]
2026-09-11 19:17 ` Kenneth Kabogo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178915233401.219967.3094305844336909787@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=elder@kernel.org \
--cc=kennethkabogo2@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.