Netdev List
 help / color / mirror / Atom feed
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

  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox