/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_ #define CHROME_COMMON_IPC_MESSAGE_UTILS_H_ #include #include #include #include #include #include #include #include "ErrorList.h" #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/pickle.h" #include "base/string_util.h" #include "build/build_config.h" #include "chrome/common/ipc_message.h" #include "mozilla/CheckedInt.h" #include "mozilla/IntegerRange.h" #if defined(OS_WIN) # include #endif template class RefPtr; template class nsCOMPtr; namespace mozilla::ipc { class IProtocol; template struct IPDLParamTraits; class SharedMemory; // Implemented in ProtocolUtils.cpp MOZ_NEVER_INLINE void PickleFatalError(const char* aMsg, IProtocol* aActor); } // namespace mozilla::ipc namespace IPC { /** * Context used to serialize into an IPC::Message. Provides relevant context * used when serializing. */ class MOZ_STACK_CLASS MessageWriter final { public: explicit MessageWriter(Message& message, mozilla::ipc::IProtocol* actor = nullptr) : message_(message), actor_(actor) {} MessageWriter(const MessageWriter&) = delete; MessageWriter& operator=(const MessageWriter&) = delete; mozilla::ipc::IProtocol* GetActor() const { return actor_; } #define FORWARD_WRITE(name, type) \ bool Write##name(const type& result) { return message_.Write##name(result); } FORWARD_WRITE(Bool, bool) FORWARD_WRITE(Int16, int16_t) FORWARD_WRITE(UInt16, uint16_t) FORWARD_WRITE(Int, int) FORWARD_WRITE(Long, long) FORWARD_WRITE(ULong, unsigned long) FORWARD_WRITE(Int32, int32_t) FORWARD_WRITE(UInt32, uint32_t) FORWARD_WRITE(Int64, int64_t) FORWARD_WRITE(UInt64, uint64_t) FORWARD_WRITE(Double, double) FORWARD_WRITE(IntPtr, intptr_t) FORWARD_WRITE(UnsignedChar, unsigned char) FORWARD_WRITE(String, std::string) FORWARD_WRITE(WString, std::wstring) #undef FORWARD_WRITE bool WriteData(const char* data, uint32_t length) { return message_.WriteData(data, length); } bool WriteBytes(const void* data, uint32_t data_len) { return message_.WriteBytes(data, data_len); } bool WriteBytesZeroCopy(void* data, uint32_t data_len, uint32_t capacity) { return message_.WriteBytesZeroCopy(data, data_len, capacity); } bool WriteSentinel(uint32_t sentinel) { return message_.WriteSentinel(sentinel); } bool WriteFileHandle(mozilla::UniqueFileHandle handle) { return message_.WriteFileHandle(std::move(handle)); } void WritePort(mozilla::ipc::ScopedPort port) { message_.WritePort(std::move(port)); } #if defined(OS_MACOSX) bool WriteMachSendRight(mozilla::UniqueMachSendRight port) { return message_.WriteMachSendRight(std::move(port)); } #endif void FatalError(const char* aErrorMsg) const { mozilla::ipc::PickleFatalError(aErrorMsg, actor_); } private: Message& message_; mozilla::ipc::IProtocol* actor_; }; /** * Context used to read data from an IPC::Message. Provides relevant context * used when deserializing and tracks iteration. */ class MOZ_STACK_CLASS MessageReader final { public: explicit MessageReader(const Message& message, mozilla::ipc::IProtocol* actor = nullptr) : message_(message), iter_(message), actor_(actor) {} MessageReader(const MessageReader&) = delete; MessageReader& operator=(const MessageReader&) = delete; mozilla::ipc::IProtocol* GetActor() const { return actor_; } #define FORWARD_READ(name, type) \ [[nodiscard]] bool Read##name(type* result) { \ return message_.Read##name(&iter_, result); \ } FORWARD_READ(Bool, bool) FORWARD_READ(Int16, int16_t) FORWARD_READ(UInt16, uint16_t) FORWARD_READ(Short, short) FORWARD_READ(Int, int) FORWARD_READ(Long, long) FORWARD_READ(ULong, unsigned long) FORWARD_READ(Int32, int32_t) FORWARD_READ(UInt32, uint32_t) FORWARD_READ(Int64, int64_t) FORWARD_READ(UInt64, uint64_t) FORWARD_READ(Double, double) FORWARD_READ(IntPtr, intptr_t) FORWARD_READ(UnsignedChar, unsigned char) FORWARD_READ(String, std::string) FORWARD_READ(WString, std::wstring) // Special version of ReadInt() which rejects negative values FORWARD_READ(Length, int); #undef FORWARD_READ [[nodiscard]] bool ReadBytesInto(void* data, uint32_t length) { return message_.ReadBytesInto(&iter_, data, length); } [[nodiscard]] bool IgnoreBytes(uint32_t length) { return message_.IgnoreBytes(&iter_, length); } [[nodiscard]] bool ReadSentinel(uint32_t sentinel) { return message_.ReadSentinel(&iter_, sentinel); } bool IgnoreSentinel() { return message_.IgnoreSentinel(&iter_); } bool HasBytesAvailable(uint32_t len) { return message_.HasBytesAvailable(&iter_, len); } void EndRead() { message_.EndRead(iter_, message_.type()); } [[nodiscard]] bool ConsumeFileHandle(mozilla::UniqueFileHandle* handle) { return message_.ConsumeFileHandle(&iter_, handle); } [[nodiscard]] bool ConsumePort(mozilla::ipc::ScopedPort* port) { return message_.ConsumePort(&iter_, port); } #if defined(OS_MACOSX) [[nodiscard]] bool ConsumeMachSendRight(mozilla::UniqueMachSendRight* port) { return message_.ConsumeMachSendRight(&iter_, port); } #endif void FatalError(const char* aErrorMsg) const { mozilla::ipc::PickleFatalError(aErrorMsg, actor_); } private: const Message& message_; PickleIterator iter_; mozilla::ipc::IProtocol* actor_; }; //----------------------------------------------------------------------------- // An iterator class for reading the fields contained within a Message. class MessageIterator { public: explicit MessageIterator(const Message& m) : msg_(m), iter_(m) {} int NextInt() const { int val; if (!msg_.ReadInt(&iter_, &val)) NOTREACHED(); return val; } intptr_t NextIntPtr() const { intptr_t val; if (!msg_.ReadIntPtr(&iter_, &val)) NOTREACHED(); return val; } const std::string NextString() const { std::string val; if (!msg_.ReadString(&iter_, &val)) NOTREACHED(); return val; } const std::wstring NextWString() const { std::wstring val; if (!msg_.ReadWString(&iter_, &val)) NOTREACHED(); return val; } private: const Message& msg_; mutable PickleIterator iter_; }; //----------------------------------------------------------------------------- // ParamTraits specializations, etc. // // The full set of types ParamTraits is specialized upon contains *possibly* // repeated types: unsigned long may be uint32_t or size_t, unsigned long long // may be uint64_t or size_t, nsresult may be uint32_t, and so on. You can't // have ParamTraits *and* ParamTraits if unsigned int // is uint32_t -- that's multiple definitions, and you can only have one. // // You could use #ifs and macro conditions to avoid duplicates, but they'd be // hairy: heavily dependent upon OS and compiler author choices, forced to // address all conflicts by hand. Happily there's a better way. The basic // idea looks like this, where T -> U represents T inheriting from U: // // class ParamTraits

// | // --> class ParamTraits1

// | // --> class ParamTraits2

// | // --> class ParamTraitsN

// or however many levels // // The default specialization of ParamTraits{M}

is an empty class that // inherits from ParamTraits{M + 1}

(or nothing in the base case). // // Now partition the set of parameter types into sets without duplicates. // Assign each set of types to a level M. Then specialize ParamTraitsM for // each of those types. A reference to ParamTraits

will consist of some // number of empty classes inheriting in sequence, ending in a non-empty // ParamTraits{N}

. It's okay for the parameter types to be duplicative: // either name of a type will resolve to the same ParamTraits{N}

. // // The nice thing is that because templates are instantiated lazily, if we // indeed have uint32_t == unsigned int, say, with the former in level N and // the latter in M > N, ParamTraitsM won't be created (as long as // nobody uses ParamTraitsM, but why would you), and no duplicate // code will be compiled or extra symbols generated. It's as efficient at // runtime as manually figuring out and avoiding conflicts by #ifs. // // The scheme we follow below names the various classes according to the types // in them, and the number of ParamTraits levels is larger, but otherwise it's // exactly the above idea. // template struct ParamTraits; template static inline void WriteParam(MessageWriter* writer, P&& p) { ParamTraits>::Write(writer, std::forward

(p)); } template static inline bool WARN_UNUSED_RESULT ReadParam(MessageReader* reader, P* p) { return ParamTraits

::Read(reader, p); } class MOZ_STACK_CLASS MessageBufferWriter { public: // Create a MessageBufferWriter to write `full_len` bytes into `writer`. // If the length exceeds a threshold, a shared memory region may be used // instead of including the data inline. // // NOTE: This does _NOT_ write out the length of the buffer. // NOTE: Data written this way _MUST_ be read using `MessageBufferReader`. MessageBufferWriter(MessageWriter* writer, uint32_t full_len); ~MessageBufferWriter(); MessageBufferWriter(const MessageBufferWriter&) = delete; MessageBufferWriter& operator=(const MessageBufferWriter&) = delete; // Write `len` bytes from `data` into the message. // // Exactly `full_len` bytes should be written across multiple calls before the // `MessageBufferWriter` is destroyed. // // WARNING: all writes (other than the last write) must be multiples of 4 // bytes in length. Not doing this will lead to padding being introduced into // the payload and break things. This can probably be improved in the future // with deeper integration between `MessageBufferWriter` and `Pickle`. bool WriteBytes(const void* data, uint32_t len); private: MessageWriter* writer_; RefPtr shmem_; char* buffer_ = nullptr; uint32_t remaining_ = 0; }; class MOZ_STACK_CLASS MessageBufferReader { public: // Create a MessageBufferReader to read `full_len` bytes from `reader` which // were written using `MessageBufferWriter`. // // NOTE: This may consume a shared memory region from the message, meaning // that the same data cannot be read multiple times. // NOTE: Data read this way _MUST_ be written using `MessageBufferWriter`. MessageBufferReader(MessageReader* reader, uint32_t full_len); ~MessageBufferReader(); MessageBufferReader(const MessageBufferReader&) = delete; MessageBufferReader& operator=(const MessageBufferReader&) = delete; // Read `count` bytes from the message into `data`. // // Exactly `full_len` bytes should be read across multiple calls before the // `MessageBufferReader` is destroyed. // // WARNING: all reads (other than the last read) must be multiples of 4 bytes // in length. Not doing this will lead to bytes being skipped in the payload // and break things. This can probably be improved in the future with deeper // integration between `MessageBufferReader` and `Pickle`. [[nodiscard]] bool ReadBytesInto(void* data, uint32_t len); private: MessageReader* reader_; RefPtr shmem_; const char* buffer_ = nullptr; uint32_t remaining_ = 0; }; // Whether or not it is safe to serialize the given type using // `WriteBytesOrShmem`. template constexpr bool kUseWriteBytes = !std::is_same_v>, bool> && (std::is_integral_v>> || std::is_floating_point_v>>); /** * Helper for writing a contiguous sequence (such as for a string or array) into * a message, with optimizations for basic integral and floating point types. * * Integral types will be copied into shared memory if the sequence exceeds 64k * bytes in size. * * Values written with this method must be read with `ReadSequenceParam`. * * The type parameter specifies the semantics to use, and should generally * either be `P&&` or `const P&`. The constness of the `data` argument should * match this parameter. */ template void WriteSequenceParam(MessageWriter* writer, std::remove_reference_t

* data, size_t length) { mozilla::CheckedUint32 ipc_length(length); if (!ipc_length.isValid()) { writer->FatalError("invalid length passed to WriteSequenceParam"); return; } writer->WriteUInt32(ipc_length.value()); if constexpr (kUseWriteBytes

) { mozilla::CheckedUint32 byte_length = ipc_length * sizeof(std::remove_reference_t

); if (!byte_length.isValid()) { writer->FatalError("invalid byte length in WriteSequenceParam"); return; } MessageBufferWriter buf_writer(writer, byte_length.value()); buf_writer.WriteBytes(data, byte_length.value()); } else { auto* end = data + length; for (auto* it = data; it != end; ++it) { WriteParam(writer, std::forward

(*it)); } } } /** * Helper for reading a contiguous sequence (such as a string or array) into a * message which was previously written using `WriteSequenceParam`. * * The function argument `allocator` will be called with the length of the * sequence, and must return a pointer to the memory region which the sequence * should be read into. */ template ()(std::declval()))>> auto WARN_UNUSED_RESULT ReadSequenceParam(MessageReader* reader, F&& allocator) -> std::enable_if_t< std::is_same_v()))>>, bool> { uint32_t length = 0; if (!reader->ReadUInt32(&length)) { reader->FatalError("failed to read byte length in ReadSequenceParam"); return false; } P* data = allocator(length); if (length == 0) { return true; } if (!data) { reader->FatalError("allocation failed in ReadSequenceParam"); return false; } if constexpr (kUseWriteBytes

) { mozilla::CheckedUint32 byte_length(length); byte_length *= sizeof(P); if (!byte_length.isValid()) { reader->FatalError("invalid byte length in ReadSequenceParam"); return false; } MessageBufferReader buf_reader(reader, byte_length.value()); return buf_reader.ReadBytesInto(data, byte_length.value()); } else { P* end = data + length; for (auto* it = data; it != end; ++it) { if (!ReadParam(reader, it)) { return false; } } return true; } } // Temporary fallback class to allow types to declare serialization using the // IPDLParamTraits type class. Will be removed once all remaining // IPDLParamTraits implementations are gone. (bug 1754009) template struct ParamTraitsIPDLFallback { template static auto Write(MessageWriter* writer, R&& p) -> decltype(mozilla::ipc::IPDLParamTraits

::Write(writer, writer->GetActor(), std::forward(p))) { mozilla::ipc::IPDLParamTraits

::Write(writer, writer->GetActor(), std::forward(p)); } template static auto Read(MessageReader* reader, R* r) -> decltype(mozilla::ipc::IPDLParamTraits

::Read(reader, reader->GetActor(), r)) { return mozilla::ipc::IPDLParamTraits

::Read(reader, reader->GetActor(), r); } }; // Fundamental types. template struct ParamTraitsFundamental : ParamTraitsIPDLFallback

{}; template <> struct ParamTraitsFundamental { typedef bool param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteBool(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadBool(r); } }; template <> struct ParamTraitsFundamental { typedef int param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteInt(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadInt(r); } }; template <> struct ParamTraitsFundamental { typedef long param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteLong(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadLong(r); } }; template <> struct ParamTraitsFundamental { typedef unsigned long param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteULong(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadULong(r); } }; template <> struct ParamTraitsFundamental { typedef long long param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteBytes(&p, sizeof(param_type)); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadBytesInto(r, sizeof(*r)); } }; template <> struct ParamTraitsFundamental { typedef unsigned long long param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteBytes(&p, sizeof(param_type)); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadBytesInto(r, sizeof(*r)); } }; template <> struct ParamTraitsFundamental { typedef double param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteDouble(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadDouble(r); } }; // Fixed-size types. template struct ParamTraitsFixed : ParamTraitsFundamental

{}; template <> struct ParamTraitsFixed { typedef int16_t param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteInt16(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadInt16(r); } }; template <> struct ParamTraitsFixed { typedef uint16_t param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteUInt16(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadUInt16(r); } }; template <> struct ParamTraitsFixed { typedef uint32_t param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteUInt32(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadUInt32(r); } }; template <> struct ParamTraitsFixed { typedef int64_t param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteInt64(p); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadInt64(r); } }; template <> struct ParamTraitsFixed { typedef uint64_t param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteInt64(static_cast(p)); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadInt64(reinterpret_cast(r)); } }; // std::* types. template struct ParamTraitsStd : ParamTraitsFixed

{}; template struct ParamTraitsStd> { typedef std::basic_string param_type; static void Write(MessageWriter* writer, const param_type& p) { WriteSequenceParam(writer, p.data(), p.size()); } static bool Read(MessageReader* reader, param_type* r) { return ReadSequenceParam(reader, [&](uint32_t length) -> T* { r->resize(length); return r->data(); }); } }; template struct ParamTraitsStd> { typedef std::map param_type; static void Write(MessageWriter* writer, const param_type& p) { WriteParam(writer, static_cast(p.size())); typename param_type::const_iterator iter; for (iter = p.begin(); iter != p.end(); ++iter) { WriteParam(writer, iter->first); WriteParam(writer, iter->second); } } static bool Read(MessageReader* reader, param_type* r) { int size; if (!ReadParam(reader, &size) || size < 0) return false; for (int i = 0; i < size; ++i) { K k; if (!ReadParam(reader, &k)) return false; V& value = (*r)[k]; if (!ReadParam(reader, &value)) return false; } return true; } }; // Windows-specific types. template struct ParamTraitsWindows : ParamTraitsStd

{}; #if defined(OS_WIN) template <> struct ParamTraitsWindows { static_assert(sizeof(HANDLE) == sizeof(intptr_t), "Wrong size for HANDLE?"); static void Write(MessageWriter* writer, HANDLE p) { writer->WriteIntPtr(reinterpret_cast(p)); } static bool Read(MessageReader* reader, HANDLE* r) { return reader->ReadIntPtr(reinterpret_cast(r)); } }; template <> struct ParamTraitsWindows { static_assert(sizeof(HWND) == sizeof(intptr_t), "Wrong size for HWND?"); static void Write(MessageWriter* writer, HWND p) { writer->WriteIntPtr(reinterpret_cast(p)); } static bool Read(MessageReader* reader, HWND* r) { return reader->ReadIntPtr(reinterpret_cast(r)); } }; #endif // defined(OS_WIN) // Various ipc/chromium types. template struct ParamTraitsIPC : ParamTraitsWindows

{}; // `UniqueFileHandle` may be serialized over IPC channels. On the receiving // side, the UniqueFileHandle is a valid duplicate of the handle which was // transmitted. // // When sending a UniqueFileHandle, the handle must be valid at the time of // transmission. As transmission is asynchronous, this requires passing // ownership of the handle to IPC. // // A UniqueFileHandle may only be read once. After it has been read once, it // will be consumed, and future reads will return an invalid handle. template <> struct ParamTraitsIPC { typedef mozilla::UniqueFileHandle param_type; static void Write(MessageWriter* writer, param_type&& p) { const bool valid = p != nullptr; WriteParam(writer, valid); if (valid) { if (!writer->WriteFileHandle(std::move(p))) { writer->FatalError("Too many file handles for one message!"); NOTREACHED() << "Too many file handles for one message!"; } } } static bool Read(MessageReader* reader, param_type* r) { bool valid; if (!ReadParam(reader, &valid)) { reader->FatalError("Error reading file handle validity"); return false; } if (!valid) { *r = nullptr; return true; } if (!reader->ConsumeFileHandle(r)) { reader->FatalError("File handle not found in message!"); return false; } return true; } }; #if defined(OS_MACOSX) // `UniqueMachSendRight` may be serialized over IPC channels. On the receiving // side, the UniqueMachSendRight is the local name of the right which was // transmitted. // // When sending a UniqueMachSendRight, the right must be valid at the time of // transmission. As transmission is asynchronous, this requires passing // ownership of the handle to IPC. // // A UniqueMachSendRight may only be read once. After it has been read once, it // will be consumed, and future reads will return an invalid right. template <> struct ParamTraitsIPC { typedef mozilla::UniqueMachSendRight param_type; static void Write(MessageWriter* writer, param_type&& p) { const bool valid = p != nullptr; WriteParam(writer, valid); if (valid) { if (!writer->WriteMachSendRight(std::move(p))) { writer->FatalError("Too many mach send rights for one message!"); NOTREACHED() << "Too many mach send rights for one message!"; } } } static bool Read(MessageReader* reader, param_type* r) { bool valid; if (!ReadParam(reader, &valid)) { reader->FatalError("Error reading mach send right validity"); return false; } if (!valid) { *r = nullptr; return true; } if (!reader->ConsumeMachSendRight(r)) { reader->FatalError("Mach send right not found in message!"); return false; } return true; } }; #endif // Mozilla-specific types. template struct ParamTraitsMozilla : ParamTraitsIPC

{}; template <> struct ParamTraitsMozilla { typedef nsresult param_type; static void Write(MessageWriter* writer, const param_type& p) { writer->WriteUInt32(static_cast(p)); } static bool Read(MessageReader* reader, param_type* r) { return reader->ReadUInt32(reinterpret_cast(r)); } }; // See comments for the IPDLParamTraits specializations for RefPtr and // nsCOMPtr for more details. template struct ParamTraitsMozilla> { static void Write(MessageWriter* writer, const RefPtr& p) { ParamTraits::Write(writer, p.get()); } static bool Read(MessageReader* reader, RefPtr* r) { return ParamTraits::Read(reader, r); } }; template struct ParamTraitsMozilla> { static void Write(MessageWriter* writer, const nsCOMPtr& p) { ParamTraits::Write(writer, p.get()); } static bool Read(MessageReader* reader, nsCOMPtr* r) { RefPtr refptr; if (!ParamTraits::Read(reader, &refptr)) { return false; } *r = std::move(refptr); return true; } }; // Finally, ParamTraits itself. template struct ParamTraits : ParamTraitsMozilla

{}; } // namespace IPC #endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_