tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Endpoint.h (10468B)


      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/ipc/MessageLink.h"
     17 #include "mozilla/ipc/ProtocolUtils.h"
     18 #include "mozilla/ipc/NodeController.h"
     19 #include "mozilla/ipc/ScopedPort.h"
     20 #include "nsXULAppAPI.h"
     21 #include "nscore.h"
     22 
     23 namespace IPC {
     24 template <class P>
     25 struct ParamTraits;
     26 }
     27 
     28 namespace mozilla {
     29 namespace ipc {
     30 
     31 namespace endpoint_detail {
     32 
     33 template <class T>
     34 static auto ActorNeedsOtherPidHelper(int)
     35    -> decltype(std::declval<T>().OtherPid(), std::true_type{});
     36 template <class>
     37 static auto ActorNeedsOtherPidHelper(long) -> std::false_type;
     38 
     39 template <typename T>
     40 constexpr bool ActorNeedsOtherPid =
     41    decltype(ActorNeedsOtherPidHelper<T>(0))::value;
     42 
     43 }  // namespace endpoint_detail
     44 
     45 struct PrivateIPDLInterface {};
     46 
     47 class UntypedEndpoint {
     48 public:
     49  UntypedEndpoint() = default;
     50 
     51  UntypedEndpoint(const PrivateIPDLInterface&, ScopedPort aPort,
     52                  const nsID& aMessageChannelId,
     53                  EndpointProcInfo aMyProcInfo = EndpointProcInfo::Invalid(),
     54                  EndpointProcInfo aOtherProcInfo = EndpointProcInfo::Invalid())
     55      : mPort(std::move(aPort)),
     56        mMessageChannelId(aMessageChannelId),
     57        mMyProcInfo(aMyProcInfo),
     58        mOtherProcInfo(aOtherProcInfo) {}
     59 
     60  UntypedEndpoint(const UntypedEndpoint&) = delete;
     61  UntypedEndpoint(UntypedEndpoint&& aOther) = default;
     62 
     63  UntypedEndpoint& operator=(const UntypedEndpoint&) = delete;
     64  UntypedEndpoint& operator=(UntypedEndpoint&& aOther) = default;
     65 
     66  // This method binds aActor to this endpoint. After this call, the actor can
     67  // be used to send and receive messages. The endpoint becomes invalid.
     68  //
     69  // If specified, aEventTarget is the target the actor will be bound to, and
     70  // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is
     71  // used.
     72  bool Bind(IToplevelProtocol* aActor,
     73            nsISerialEventTarget* aEventTarget = nullptr) {
     74    MOZ_RELEASE_ASSERT(IsValid());
     75    MOZ_RELEASE_ASSERT(mMyProcInfo == EndpointProcInfo::Invalid() ||
     76                       mMyProcInfo == EndpointProcInfo::Current());
     77    MOZ_RELEASE_ASSERT(!aEventTarget || aEventTarget->IsOnCurrentThread());
     78    return aActor->Open(std::move(mPort), mMessageChannelId, mOtherProcInfo,
     79                        aEventTarget);
     80  }
     81 
     82  bool IsValid() const { return mPort.IsValid(); }
     83 
     84 protected:
     85  friend struct IPC::ParamTraits<UntypedEndpoint>;
     86 
     87  ScopedPort mPort;
     88  nsID mMessageChannelId{};
     89  EndpointProcInfo mMyProcInfo;
     90  EndpointProcInfo mOtherProcInfo;
     91 };
     92 
     93 /**
     94 * An endpoint represents one end of a partially initialized IPDL channel. To
     95 * set up a new top-level protocol:
     96 *
     97 * Endpoint<PFooParent> parentEp;
     98 * Endpoint<PFooChild> childEp;
     99 * nsresult rv;
    100 * rv = PFoo::CreateEndpoints(&parentEp, &childEp);
    101 *
    102 * Endpoints can be passed in IPDL messages or sent to other threads using
    103 * PostTask. Once an Endpoint has arrived at its destination process and thread,
    104 * you need to create the top-level actor and bind it to the endpoint:
    105 *
    106 * FooParent* parent = new FooParent();
    107 * bool rv1 = parentEp.Bind(parent, processActor);
    108 * bool rv2 = parent->SendBar(...);
    109 *
    110 * (See Bind below for an explanation of processActor.) Once the actor is bound
    111 * to the endpoint, it can send and receive messages.
    112 *
    113 * If creating endpoints for a [NeedsOtherPid] actor, you're required to also
    114 * pass in parentPid and childPid, which are the pids of the processes in which
    115 * the parent and child endpoints will be used.
    116 */
    117 template <class PFooSide>
    118 class Endpoint final : public UntypedEndpoint {
    119 public:
    120  using UntypedEndpoint::IsValid;
    121  using UntypedEndpoint::UntypedEndpoint;
    122 
    123  EndpointProcInfo OtherEndpointProcInfo() const {
    124    static_assert(
    125        endpoint_detail::ActorNeedsOtherPid<PFooSide>,
    126        "OtherPid may only be called on Endpoints for actors which are "
    127        "[NeedsOtherPid]");
    128    MOZ_RELEASE_ASSERT(mOtherProcInfo != EndpointProcInfo::Invalid());
    129    return mOtherProcInfo;
    130  }
    131 
    132  base::ProcessId OtherPid() const { return OtherEndpointProcInfo().mPid; }
    133 
    134  GeckoChildID OtherChildID() const { return OtherEndpointProcInfo().mChildID; }
    135 
    136  // This method binds aActor to this endpoint. After this call, the actor can
    137  // be used to send and receive messages. The endpoint becomes invalid.
    138  //
    139  // If specified, aEventTarget is the target the actor will be bound to, and
    140  // must be on the current thread. Otherwise, GetCurrentSerialEventTarget() is
    141  // used.
    142  bool Bind(PFooSide* aActor, nsISerialEventTarget* aEventTarget = nullptr) {
    143    return UntypedEndpoint::Bind(aActor, aEventTarget);
    144  }
    145 };
    146 
    147 #if defined(XP_MACOSX)
    148 void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag, int error);
    149 #else
    150 inline void AnnotateCrashReportWithErrno(CrashReporter::Annotation tag,
    151                                         int error) {}
    152 #endif
    153 
    154 // This function is used internally to create a pair of Endpoints. See the
    155 // comment above Endpoint for a description of how it might be used.
    156 template <class PFooParent, class PFooChild>
    157 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
    158                         Endpoint<PFooParent>* aParentEndpoint,
    159                         Endpoint<PFooChild>* aChildEndpoint) {
    160  static_assert(
    161      !endpoint_detail::ActorNeedsOtherPid<PFooParent> &&
    162          !endpoint_detail::ActorNeedsOtherPid<PFooChild>,
    163      "Pids are required when creating endpoints for [NeedsOtherPid] actors");
    164 
    165  auto [parentPort, childPort] =
    166      NodeController::GetSingleton()->CreatePortPair();
    167  nsID channelId = nsID::GenerateUUID();
    168  *aParentEndpoint =
    169      Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId);
    170  *aChildEndpoint =
    171      Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId);
    172  return NS_OK;
    173 }
    174 
    175 template <class PFooParent, class PFooChild>
    176 nsresult CreateEndpoints(const PrivateIPDLInterface& aPrivate,
    177                         EndpointProcInfo aParentDestProcInfo,
    178                         EndpointProcInfo aChildDestProcInfo,
    179                         Endpoint<PFooParent>* aParentEndpoint,
    180                         Endpoint<PFooChild>* aChildEndpoint) {
    181  MOZ_RELEASE_ASSERT(aParentDestProcInfo != EndpointProcInfo::Invalid());
    182  MOZ_RELEASE_ASSERT(aChildDestProcInfo != EndpointProcInfo::Invalid());
    183 
    184  auto [parentPort, childPort] =
    185      NodeController::GetSingleton()->CreatePortPair();
    186  nsID channelId = nsID::GenerateUUID();
    187  *aParentEndpoint =
    188      Endpoint<PFooParent>(aPrivate, std::move(parentPort), channelId,
    189                           aParentDestProcInfo, aChildDestProcInfo);
    190  *aChildEndpoint =
    191      Endpoint<PFooChild>(aPrivate, std::move(childPort), channelId,
    192                          aChildDestProcInfo, aParentDestProcInfo);
    193  return NS_OK;
    194 }
    195 
    196 class UntypedManagedEndpoint {
    197 public:
    198  bool IsValid() const { return mInner.isSome(); }
    199 
    200  UntypedManagedEndpoint(const UntypedManagedEndpoint&) = delete;
    201  UntypedManagedEndpoint& operator=(const UntypedManagedEndpoint&) = delete;
    202 
    203 protected:
    204  UntypedManagedEndpoint() = default;
    205  explicit UntypedManagedEndpoint(IProtocol* aActor);
    206 
    207  UntypedManagedEndpoint(UntypedManagedEndpoint&& aOther) noexcept
    208      : mInner(std::move(aOther.mInner)) {
    209    aOther.mInner = Nothing();
    210  }
    211  UntypedManagedEndpoint& operator=(UntypedManagedEndpoint&& aOther) noexcept {
    212    this->~UntypedManagedEndpoint();
    213    new (this) UntypedManagedEndpoint(std::move(aOther));
    214    return *this;
    215  }
    216 
    217  ~UntypedManagedEndpoint() noexcept;
    218 
    219  bool BindCommon(IProtocol* aActor, IRefCountedProtocol* aManager);
    220 
    221 private:
    222  friend struct IPC::ParamTraits<UntypedManagedEndpoint>;
    223 
    224  struct Inner {
    225    // Pointers to the toplevel actor which will manage this connection. When
    226    // created, only `mOtherSide` will be set, and will reference the
    227    // toplevel actor which the other side is managed by. After being sent over
    228    // IPC, only `mToplevel` will be set, and will be the toplevel actor for the
    229    // channel which received the IPC message.
    230    RefPtr<WeakActorLifecycleProxy> mOtherSide;
    231    RefPtr<WeakActorLifecycleProxy> mToplevel;
    232 
    233    ActorId mId = 0;
    234    ProtocolId mType = LastMsgIndex;
    235    ActorId mManagerId = 0;
    236    ProtocolId mManagerType = LastMsgIndex;
    237  };
    238  Maybe<Inner> mInner;
    239 };
    240 
    241 /**
    242 * A managed endpoint represents one end of a partially initialized managed
    243 * IPDL actor. It is used for situations where the usual IPDL Constructor
    244 * methods do not give sufficient control over the construction of actors, such
    245 * as when constructing actors within replies, or constructing multiple related
    246 * actors simultaneously.
    247 *
    248 * FooParent* parent = new FooParent();
    249 * ManagedEndpoint<PFooChild> childEp = parentMgr->OpenPFooEndpoint(parent);
    250 *
    251 * ManagedEndpoints should be sent using IPDL messages or other mechanisms to
    252 * the other side of the manager channel. Once the ManagedEndpoint has arrived
    253 * at its destination, you can create the actor, and bind it to the endpoint.
    254 *
    255 * FooChild* child = new FooChild();
    256 * childMgr->BindPFooEndpoint(childEp, child);
    257 *
    258 * WARNING: If the remote side of an endpoint has not been bound before it
    259 * begins to receive messages, an IPC routing error will occur, likely causing
    260 * a browser crash.
    261 */
    262 template <class PFooSide>
    263 class ManagedEndpoint : public UntypedManagedEndpoint {
    264 public:
    265  ManagedEndpoint() = default;
    266  ManagedEndpoint(ManagedEndpoint&&) noexcept = default;
    267  ManagedEndpoint& operator=(ManagedEndpoint&&) noexcept = default;
    268 
    269  ManagedEndpoint(const PrivateIPDLInterface&, IProtocol* aActor)
    270      : UntypedManagedEndpoint(aActor) {}
    271 
    272  bool Bind(const PrivateIPDLInterface&, PFooSide* aActor,
    273            IRefCountedProtocol* aManager) {
    274    return BindCommon(aActor, aManager);
    275  }
    276 
    277  // Only invalid ManagedEndpoints can be equal, as valid endpoints are unique.
    278  bool operator==(const ManagedEndpoint& _o) const {
    279    return !IsValid() && !_o.IsValid();
    280  }
    281 };
    282 
    283 }  // namespace ipc
    284 }  // namespace mozilla
    285 
    286 #endif  // IPC_GLUE_ENDPOINT_H_