1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
6 
7 #ifndef IPC_GLUE_ENDPOINT_H_
8 #define IPC_GLUE_ENDPOINT_H_
9 
10 #include <utility>
11 #include "CrashAnnotations.h"
12 #include "base/process.h"
13 #include "base/process_util.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/ipc/MessageLink.h"
18 #include "mozilla/ipc/ProtocolUtils.h"
19 #include "mozilla/ipc/NodeController.h"
20 #include "mozilla/ipc/ScopedPort.h"
21 #include "nsXULAppAPI.h"
22 #include "nscore.h"
23 
24 namespace IPC {
25 template <class P>
26 struct ParamTraits;
27 }
28 
29 namespace mozilla {
30 namespace ipc {
31 
32 namespace endpoint_detail {
33 
34 template <class T>
35 static auto ActorNeedsOtherPidHelper(int)
36     -> decltype(std::declval<T>().OtherPid(), std::true_type{});
37 template <class>
38 static auto ActorNeedsOtherPidHelper(long) -> std::false_type;
39 
40 template <typename T>
41 constexpr bool ActorNeedsOtherPid =
42     decltype(ActorNeedsOtherPidHelper<T>(0))::value;
43 
44 }  // namespace endpoint_detail
45 
46 struct PrivateIPDLInterface {};
47 
48 /**
49  * An endpoint represents one end of a partially initialized IPDL channel. To
50  * set up a new top-level protocol:
51  *
52  * Endpoint<PFooParent> parentEp;
53  * Endpoint<PFooChild> childEp;
54  * nsresult rv;
55  * rv = PFoo::CreateEndpoints(&parentEp, &childEp);
56  *
57  * Endpoints can be passed in IPDL messages or sent to other threads using
58  * PostTask. Once an Endpoint has arrived at its destination process and thread,
59  * you need to create the top-level actor and bind it to the endpoint:
60  *
61  * FooParent* parent = new FooParent();
62  * bool rv1 = parentEp.Bind(parent, processActor);
63  * bool rv2 = parent->SendBar(...);
64  *
65  * (See Bind below for an explanation of processActor.) Once the actor is bound
66  * to the endpoint, it can send and receive messages.
67  *
68  * If creating endpoints for a [NeedsOtherPid] actor, you're required to also
69  * pass in parentPid and childPid, which are the pids of the processes in which
70  * the parent and child endpoints will be used.
71  */
72 template <class PFooSide>
73 class Endpoint {
74  public:
75   using ProcessId = base::ProcessId;
76 
77   Endpoint() = default;
78 
79   Endpoint(const PrivateIPDLInterface&, ScopedPort aPort,
80            ProcessId aMyPid = base::kInvalidProcessId,
81            ProcessId aOtherPid = base::kInvalidProcessId)
mPort(std::move (aPort))82       : mPort(std::move(aPort)), mMyPid(aMyPid), mOtherPid(aOtherPid) {}
83 
84   Endpoint(const Endpoint&) = delete;
85   Endpoint(Endpoint&& aOther) = default;
86 
87   Endpoint& operator=(const Endpoint&) = delete;
88   Endpoint& operator=(Endpoint&& aOther) = default;
89 
OtherPid()90   base::ProcessId OtherPid() const {
91     static_assert(
92         endpoint_detail::ActorNeedsOtherPid<PFooSide>,
93         "OtherPid may only be called on Endpoints for actors which are "
94         "[NeedsOtherPid]");
95     MOZ_RELEASE_ASSERT(mOtherPid != base::kInvalidProcessId);
96     return mOtherPid;
97   }
98 
99   // This method binds aActor to this endpoint. After this call, the actor can
100   // be used to send and receive messages. The endpoint becomes invalid.
Bind(PFooSide * aActor)101   bool Bind(PFooSide* aActor) {
102     MOZ_RELEASE_ASSERT(IsValid());
103     MOZ_RELEASE_ASSERT(mMyPid == base::kInvalidProcessId ||
104                        mMyPid == base::GetCurrentProcId());
105     return aActor->Open(std::move(mPort), mOtherPid);
106   }
107 
IsValid()108   bool IsValid() const { return mPort.IsValid(); }
109 
110  private:
111   friend struct IPC::ParamTraits<Endpoint<PFooSide>>;
112 
113   ScopedPort mPort;
114   ProcessId mMyPid = base::kInvalidProcessId;
115   ProcessId mOtherPid = base::kInvalidProcessId;
116 };
117 
118 #if defined(XP_MACOSX)
119 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error);
120 #else
121 inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag,
122                                          int error) {}
123 #endif
124 
125 // This function is used internally to create a pair of Endpoints. See the
126 // comment above Endpoint for a description of how it might be used.
127 template <class PFooParent, class PFooChild>
128 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
129                          Endpoint<PFooParent>* aParentEndpoint,
130                          Endpoint<PFooChild>* aChildEndpoint) {
131   static_assert(
132       !endpoint_detail::ActorNeedsOtherPid<PFooParent> &&
133           !endpoint_detail::ActorNeedsOtherPid<PFooChild>,
134       "Pids are required when creating endpoints for [NeedsOtherPid] actors");
135 
136   auto [parentPort, childPort] =
137       NodeController::GetSingleton()->CreatePortPair();
138   *aParentEndpoint = Endpoint<PFooParent>(aPrivate, std::move(parentPort));
139   *aChildEndpoint = Endpoint<PFooChild>(aPrivate, std::move(childPort));
140   return NS_OK;
141 }
142 
143 template <class PFooParent, class PFooChild>
144 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
145                          base::ProcessId aParentDestPid,
146                          base::ProcessId aChildDestPid,
147                          Endpoint<PFooParent>* aParentEndpoint,
148                          Endpoint<PFooChild>* aChildEndpoint) {
149   MOZ_RELEASE_ASSERT(aParentDestPid != base::kInvalidProcessId);
150   MOZ_RELEASE_ASSERT(aChildDestPid != base::kInvalidProcessId);
151 
152   auto [parentPort, childPort] =
153       NodeController::GetSingleton()->CreatePortPair();
154   *aParentEndpoint = Endpoint<PFooParent>(aPrivate, std::move(parentPort),
155                                           aParentDestPid, aChildDestPid);
156   *aChildEndpoint = Endpoint<PFooChild>(aPrivate, std::move(childPort),
157                                         aChildDestPid, aParentDestPid);
158   return NS_OK;
159 }
160 
161 class UntypedManagedEndpoint {
162  public:
163   bool IsValid() const { return mInner.isSome(); }
164 
165   UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete;
166   UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete;
167 
168  protected:
169   UntypedManagedEndpoint() = default;
170   explicit UntypedManagedEndpoint(IProtocol* aActor);
171 
172   UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept
173       : mInner(std::move(aOther.mInner)) {
174     aOther.mInner = Nothing();
175   }
176   UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept {
177     this->~UntypedManagedEndpoint();
178     new (this) UntypedManagedEndpoint(std::move(aOther));
179     return *this;
180   }
181 
182   ~UntypedManagedEndpoint() noexcept;
183 
184   bool BindCommon(IProtocol* aActor, IProtocol* aManager);
185 
186  private:
187   friend struct IPDLParamTraits<UntypedManagedEndpoint>;
188 
189   struct Inner {
190     // Pointers to the toplevel actor which will manage this connection. When
191     // created, only `mOtherSide` will be set, and will reference the
192     // toplevel actor which the other side is managed by. After being sent over
193     // IPC, only `mToplevel` will be set, and will be the toplevel actor for the
194     // channel which received the IPC message.
195     RefPtr<WeakActorLifecycleProxy> mOtherSide;
196     RefPtr<WeakActorLifecycleProxy> mToplevel;
197 
198     int32_t mId = 0;
199     ProtocolId mType = LastMsgIndex;
200     int32_t mManagerId = 0;
201     ProtocolId mManagerType = LastMsgIndex;
202   };
203   Maybe<Inner> mInner;
204 };
205 
206 /**
207  * A managed endpoint represents one end of a partially initialized managed
208  * IPDL actor. It is used for situations where the usual IPDL Constructor
209  * methods do not give sufficient control over the construction of actors, such
210  * as when constructing actors within replies, or constructing multiple related
211  * actors simultaneously.
212  *
213  * FooParent* parent = new FooParent();
214  * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent);
215  *
216  * ManagedEndpoints should be sent using IPDL messages or other mechanisms to
217  * the other side of the manager channel. Once the ManagedEndpoint has arrived
218  * at its destination, you can create the actor, and bind it to the endpoint.
219  *
220  * FooChild* child = new FooChild();
221  * childMgr->BindPFooEndpoint(childEp, child);
222  *
223  * WARNING: If the remote side of an endpoint has not been bound before it
224  * begins to receive messages, an IPC routing error will occur, likely causing
225  * a browser crash.
226  */
227 template <class PFooSide>
228 class ManagedEndpoint : public UntypedManagedEndpoint {
229  public:
230   ManagedEndpoint() = default;
231   ManagedEndpoint(ManagedEndpoint&&) noexcept = default;
232   ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default;
233 
234   ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor)
235       : UntypedManagedEndpoint(aActor) {}
236 
237   bool Bind(const PrivateIPDLInterface&, PFooSide* aActor, IProtocol* aManager,
238             ManagedContainer<PFooSide>& aContainer) {
239     if (!BindCommon(aActor, aManager)) {
240       return false;
241     }
242     aContainer.Insert(aActor);
243     return true;
244   }
245 
246   // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique.
247   bool operator==(const ManagedEndpoint& _o) const {
248     return !IsValid() && !_o.IsValid();
249   }
250 };
251 
252 }  // namespace ipc
253 }  // namespace mozilla
254 
255 #endif  // IPC_GLUE_ENDPOINT_H_
256