Do mojo remotes have unique IDs associated with them?

1,559 views
Skip to first unread message

Niko Tsirakis

unread,
Apr 3, 2024, 4:33:59 PM4/3/24
to chromi...@chromium.org
Hello,

I asked this question internally (YAQS) before realizing there was a mailing list. Copying the details below:

---------------------------------------------

I have a receiver implementation class (let's call it MyImpl) that accepts a PendingRemote (let's call it MyRemote) as one of its mojo methods. When the remote is bound, I'd like to immediately make an async call to one of its getters (GetID) so that I can organize all Remotes that share the same ID. This creates some awkward logic, however:

  1. MyImpl receives the pending remote and binds it.
  2. MyImpl calls MyRemote()->GetID(callback)to get its ID, which should be constant for the lifetime of the remote.
  3. MyImpl also adds MyRemote to a list of remotes to keep the object alive while we wait for a response.
  4. The ID callback fires with the Remote's ID. Now we need to sort the original Remote into a bucket that matches the returned ID.

The question is - how do we access and/or find the remote that was added in step 3 so we can sort it? Is there a UUID or some other property I can pass to the callback to identify it?

Even broader question - is there a better way to do this? We can alternatively add the ID as an argument to the impl method that accepts the PendingRemote, but that is also problematic as the caller doesn't technically have to use the ID that is associated with the remote object (and we want them to).

Ken Rockot

unread,
Apr 3, 2024, 4:44:55 PM4/3/24
to Niko Tsirakis, chromi...@chromium.org
On Wed, Apr 3, 2024 at 1:34 PM 'Niko Tsirakis' via chromium-mojo <chromi...@chromium.org> wrote:
Hello,

I asked this question internally (YAQS) before realizing there was a mailing list. Copying the details below:

---------------------------------------------

I have a receiver implementation class (let's call it MyImpl) that accepts a PendingRemote (let's call it MyRemote) as one of its mojo methods. When the remote is bound, I'd like to immediately make an async call to one of its getters (GetID) so that I can organize all Remotes that share the same ID. This creates some awkward logic, however:

  1. MyImpl receives the pending remote and binds it.
  2. MyImpl calls MyRemote()->GetID(callback)to get its ID, which should be constant for the lifetime of the remote.
  3. MyImpl also adds MyRemote to a list of remotes to keep the object alive while we wait for a response.
  4. The ID callback fires with the Remote's ID. Now we need to sort the original Remote into a bucket that matches the returned ID.

The question is - how do we access and/or find the remote that was added in step 3 so we can sort it? Is there a UUID or some other property I can pass to the callback to identify it?

There is no such ID.

However, you are giving GetID a callback; so you can always generate an arbitrary local ID within your impl, store the remote reference in a map keyed by that ID, and bind the same ID as an argument on the callback you give to GetID. Then when the callback is called, you'll have both the local ID and the remote ID for that specific request.

Having said all that - this sounds pretty error prone, as do most general schemes that involve associating IPC endpoints with IDs of some kind. May I ask how/why this ID is necessary?
 

Even broader question - is there a better way to do this? We can alternatively add the ID as an argument to the impl method that accepts the PendingRemote, but that is also problematic as the caller doesn't technically have to use the ID that is associated with the remote object (and we want them to).

--
You received this message because you are subscribed to the Google Groups "chromium-mojo" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-moj...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-mojo/CAHb-o4yo35LXhdrwr%2BCs7qn%3DoFg5bapWUFTGTw30rgDCdsYF%3DQ%40mail.gmail.com.

Daniel Cheng

unread,
Apr 3, 2024, 4:45:58 PM4/3/24
to Niko Tsirakis, chromi...@chromium.org
This is pretty abstract and it's hard to give a good answer without understanding what the higher-level purpose is here.

Internally, there are identifiers for Mojo endpoints, but they are not exposed to application code.

In the most ideal world of all, you would not pass the ID over Mojo at all. The ID would be implicitly associated on the Receiver side; if you don't pass it over Mojo, you don't need to validate it (or worry about the caller ignoring it).
In a moderately ideal world, we should just not create multiple remotes per ID. We should just use one remote per ID.
In a less ideal world where this is unavoidable, we should at least pass the ID with the PendingRemote to avoid the async callback (this is mentioned in your message above).

The answer to your immediate question here is:
- you could register your PendingRemote in a RemoteSet, which gives you an opaque remote ID
- you then maintain your own map of IDs to remote IDs to bucket remotes with the same ID together

But this comes with additional complexity and statefulness and room for error: you have to register a disconnect handler with the remote set to clean up stale entries your ID -> remote association.

Can you give a high-level explanation of what you're trying to do? There may be a better solution.

Daniel

--

Dave Tapuska

unread,
Apr 3, 2024, 4:46:33 PM4/3/24
to Ken Rockot, Niko Tsirakis, chromi...@chromium.org
Also you don't need to store the remote in a list. You could bind it to the callback that you are getting the response from. I've seen that done a few places. (https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/printing/usb_printer_util.cc;l=338;drc=c3a520cd08d04365a91c6837b947dfa714cfbfd5) is one example.

dave.

Niko Tsirakis

unread,
Apr 3, 2024, 6:52:34 PM4/3/24
to Daniel Cheng, chromi...@chromium.org
Yes - sorry for the vagueness! I'm not sure what the visibility of this mailing list is, nor am I sure what I'm allowed to divulge for internal teams, so I'm erring on the side of caution. I've added more details below with some remaining ambiguity (please ignore the original example):

The Remotes contain two methods: one that returns a regex string for pattern matching, and one that contains a reference to a remote callback that should be fired when the regex is a match. In order for this Remote to be useful, we have to immediately ask it what the regex is so that we can trigger the callback later. When we get the regex, we have to then associate it back to the right remote. And I'd rather not have the caller pass in the regex as an arg, since it will technically not need to be == to the regex that's contained receiver-side, so we open ourselves to errors there.

dtapuska@ provided an interesting reference where the Remote is bound to the callback. I wasn't aware you could do that - is this an appropriate solution? Then when the callback is fired, we can permanently store the Remote + the details where needed. I just don't know what happens if the Remote disconnects midway through that process.

Daniel Cheng

unread,
Apr 3, 2024, 11:38:39 PM4/3/24
to Niko Tsirakis, chromi...@chromium.org
On Wed, 3 Apr 2024 at 15:52, Niko Tsirakis <ntsi...@google.com> wrote:
Yes - sorry for the vagueness! I'm not sure what the visibility of this mailing list is, nor am I sure what I'm allowed to divulge for internal teams, so I'm erring on the side of caution. I've added more details below with some remaining ambiguity (please ignore the original example):

The Remotes contain two methods: one that returns a regex string for pattern matching, and one that contains a reference to a remote callback that should be fired when the regex is a match. In order for this Remote to be useful, we have to immediately ask it what the regex is so that we can trigger the callback later. When we get the regex, we have to then associate it back to the right remote. And I'd rather not have the caller pass in the regex as an arg, since it will technically not need to be == to the regex that's contained receiver-side, so we open ourselves to errors there.

So the scenario is:
- we have two processes, A and B.
- process A calls process B with WaitForMatch(pending_remote<MatchedCallback> remote).
- process B calls GetRegex() => (string regex) to get the regex from process A.
- process B then waits for a match on `regex` and invokes MatchedCallback::DidMatch() on a match.

But from a security/correctness perspective, this seems pretty much the same as:
- process A calls process B with WaitForMatch(string regex, pending_remote<MatchedCallback> remote)
- process B waits for a match on `regex` and invokes MatchedCallback::DidMatch() on a match.

In both scenarios, process B is responsible for handling the regex matching. And in both scenarios, process B (if untrustworthy) can choose to ignore the regex and do its own thing, right? So I don't feel like it makes a difference where/when the regex is passed, so preferring the simpler option seems better.

Also, there are some security implications to passing regex across IPC. I'd suggest looping in security reviewers sooner rather than later: in the past, I believe we've tried to avoid trusting regex from an untrustworthy source (e.g. see https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/public/mojom/safe_url_pattern.mojom;l=9;drc=18196c6a774d2d25f45ce31a8e3bb7225e72943a).


dtapuska@ provided an interesting reference where the Remote is bound to the callback. I wasn't aware you could do that - is this an appropriate solution? Then when the callback is fired, we can permanently store the Remote + the details where needed. I just don't know what happens if the Remote disconnects midway through that process.

Assuming everything works correctly, the callback + remote will vanish into thin air. But I don't think this is needed (see above), unless I'm missing something.

Daniel

Niko Tsirakis

unread,
Apr 4, 2024, 9:47:00 AM4/4/24
to Daniel Cheng, chromi...@chromium.org
On Wed, Apr 3, 2024 at 11:38 PM Daniel Cheng <dch...@chromium.org> wrote:
But from a security/correctness perspective, this seems pretty much the same as:
- process A calls process B with WaitForMatch(string regex, pending_remote<MatchedCallback> remote)
- process B waits for a match on `regex` and invokes MatchedCallback::DidMatch() on a match.

In both scenarios, process B is responsible for handling the regex matching. And in both scenarios, process B (if untrustworthy) can choose to ignore the regex and do its own thing, right? So I don't feel like it makes a difference where/when the regex is passed, so preferring the simpler option seems better.
 
Ah okay, so I guess there really is no better way to handle this than passing it as an arg. Figured I'd double check before making changes to the interface. Thanks for the insight!

Also, there are some security implications to passing regex across IPC. I'd suggest looping in security reviewers sooner rather than later: in the past, I believe we've tried to avoid trusting regex from an untrustworthy source (e.g. see https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/public/mojom/safe_url_pattern.mojom;l=9;drc=18196c6a774d2d25f45ce31a8e3bb7225e72943a).

Thanks for the resource. We have plans to safeguard this interface further, but that work is still TBD.
Reply all
Reply to author
Forward
0 new messages