// Copyright (c) 2012 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. #include "ipc/ipc_message_utils.h" #include #include #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/unguessable_token.h" #include "build/build_config.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_attachment.h" #include "ipc/ipc_message_attachment_set.h" #include "ipc/ipc_mojo_param_traits.h" #if BUILDFLAG(IS_APPLE) #include "ipc/mach_port_mac.h" #endif #if BUILDFLAG(IS_WIN) #include #include "ipc/handle_win.h" #include "ipc/ipc_platform_file.h" #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include "base/file_descriptor_posix.h" #include "ipc/ipc_platform_file_attachment_posix.h" #endif #if BUILDFLAG(IS_FUCHSIA) #include "base/fuchsia/fuchsia_logging.h" #include "ipc/handle_attachment_fuchsia.h" #endif #if BUILDFLAG(IS_ANDROID) #include "base/android/scoped_hardware_buffer_handle.h" #include "ipc/ipc_mojo_handle_attachment.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/scope_to_message_pipe.h" #endif namespace IPC { namespace { const int kMaxRecursionDepth = 200; template void LogBytes(const std::vector& data, std::string* out) { #if BUILDFLAG(IS_WIN) // Windows has a GUI for logging, which can handle arbitrary binary data. for (size_t i = 0; i < data.size(); ++i) out->push_back(data[i]); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // On POSIX, we log to stdout, which we assume can display ASCII. static const size_t kMaxBytesToLog = 100; for (size_t i = 0; i < std::min(data.size(), kMaxBytesToLog); ++i) { if (isprint(data[i])) out->push_back(data[i]); else out->append( base::StringPrintf("[%02X]", static_cast(data[i]))); } if (data.size() > kMaxBytesToLog) { out->append(base::StringPrintf( " and %u more bytes", static_cast(data.size() - kMaxBytesToLog))); } #endif } void WriteValue(const base::Value& value, int recursion, base::Pickle* pickle); void WriteDictValue(const base::Value::Dict& value, int recursion, base::Pickle* pickle) { WriteParam(pickle, base::checked_cast(value.size())); for (const auto entry : value) { WriteParam(pickle, entry.first); WriteValue(entry.second, recursion + 1, pickle); } } void WriteListValue(const base::Value::List& value, int recursion, base::Pickle* pickle) { WriteParam(pickle, base::checked_cast(value.size())); for (const auto& entry : value) { WriteValue(entry, recursion + 1, pickle); } } void WriteValue(const base::Value& value, int recursion, base::Pickle* pickle) { bool result; if (recursion > kMaxRecursionDepth) { LOG(ERROR) << "Max recursion depth hit in WriteValue."; return; } pickle->WriteInt(static_cast(value.type())); switch (value.type()) { case base::Value::Type::NONE: break; case base::Value::Type::BOOLEAN: { WriteParam(pickle, value.GetBool()); break; } case base::Value::Type::INTEGER: { DCHECK(value.is_int()); WriteParam(pickle, value.GetInt()); break; } case base::Value::Type::DOUBLE: { DCHECK(value.is_int() || value.is_double()); WriteParam(pickle, value.GetDouble()); break; } case base::Value::Type::STRING: { const std::string* val = value.GetIfString(); result = !!val; DCHECK(result); WriteParam(pickle, *val); break; } case base::Value::Type::BINARY: { pickle->WriteData(reinterpret_cast(value.GetBlob().data()), value.GetBlob().size()); break; } case base::Value::Type::DICT: { DCHECK(value.is_dict()); WriteDictValue(value.GetDict(), recursion, pickle); break; } case base::Value::Type::LIST: { DCHECK(value.is_list()); WriteListValue(value.GetList(), recursion, pickle); break; } } } bool ReadValue(const base::Pickle* pickle, base::PickleIterator* iter, int recursion, base::Value* value); // Helper for ReadValue that reads a Value::Dict into a pre-allocated object. bool ReadDictValue(const base::Pickle* pickle, base::PickleIterator* iter, int recursion, base::Value::Dict* value) { int size; if (!ReadParam(pickle, iter, &size)) return false; for (int i = 0; i < size; i++) { std::string key; base::Value subvalue; if (!ReadParam(pickle, iter, &key) || !ReadValue(pickle, iter, recursion + 1, &subvalue)) { return false; } value->Set(key, std::move(subvalue)); } return true; } // Helper for ReadValue that reads a Value::List into a pre-allocated object. bool ReadListValue(const base::Pickle* pickle, base::PickleIterator* iter, int recursion, base::Value::List* value) { int size; if (!ReadParam(pickle, iter, &size)) return false; value->reserve(size); for (int i = 0; i < size; i++) { base::Value subval; if (!ReadValue(pickle, iter, recursion + 1, &subval)) return false; value->Append(std::move(subval)); } return true; } bool ReadValue(const base::Pickle* pickle, base::PickleIterator* iter, int recursion, base::Value* value) { if (recursion > kMaxRecursionDepth) { LOG(ERROR) << "Max recursion depth hit in ReadValue."; return false; } int type; if (!ReadParam(pickle, iter, &type)) return false; constexpr int kMinValueType = static_cast(base::Value::Type::NONE); constexpr int kMaxValueType = static_cast(base::Value::Type::LIST); if (type > kMaxValueType || type < kMinValueType) return false; switch (static_cast(type)) { case base::Value::Type::NONE: *value = base::Value(); break; case base::Value::Type::BOOLEAN: { bool val; if (!ReadParam(pickle, iter, &val)) return false; *value = base::Value(val); break; } case base::Value::Type::INTEGER: { int val; if (!ReadParam(pickle, iter, &val)) return false; *value = base::Value(val); break; } case base::Value::Type::DOUBLE: { double val; if (!ReadParam(pickle, iter, &val)) return false; *value = base::Value(val); break; } case base::Value::Type::STRING: { std::string val; if (!ReadParam(pickle, iter, &val)) return false; *value = base::Value(std::move(val)); break; } case base::Value::Type::BINARY: { base::span data; if (!iter->ReadData(&data)) return false; *value = base::Value(data); break; } case base::Value::Type::DICTIONARY: { base::Value::Dict val; if (!ReadDictValue(pickle, iter, recursion, &val)) return false; *value = base::Value(std::move(val)); break; } case base::Value::Type::LIST: { base::Value::List val; if (!ReadListValue(pickle, iter, recursion, &val)) return false; *value = base::Value(std::move(val)); break; } default: NOTREACHED(); return false; } return true; } } // namespace // ----------------------------------------------------------------------------- LogData::LogData() : routing_id(0), type(0), sent(0), receive(0), dispatch(0) { } LogData::LogData(const LogData& other) = default; LogData::~LogData() = default; void ParamTraits::Log(const param_type& p, std::string* l) { l->append(p ? "true" : "false"); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteBytes(&p, sizeof(param_type)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char* data; if (!iter->ReadBytes(&data, sizeof(param_type))) return false; memcpy(r, data, sizeof(param_type)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteBytes(&p, sizeof(param_type)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char* data; if (!iter->ReadBytes(&data, sizeof(param_type))) return false; memcpy(r, data, sizeof(param_type)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteBytes(&p, sizeof(param_type)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char* data; if (!iter->ReadBytes(&data, sizeof(param_type))) return false; memcpy(r, data, sizeof(param_type)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ BUILDFLAG(IS_FUCHSIA) || \ (BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_64_BITS)) void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } #endif void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::NumberToString(p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::StringPrintf("%e", p)); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteBytes(reinterpret_cast(&p), sizeof(param_type)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char *data; if (!iter->ReadBytes(&data, sizeof(*r))) { NOTREACHED(); return false; } memcpy(r, data, sizeof(param_type)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::StringPrintf("%e", p)); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(p); } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::UTF16ToUTF8(p)); } #if BUILDFLAG(IS_WIN) bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { base::StringPiece16 piece16; if (!iter->ReadStringPiece16(&piece16)) return false; *r = base::AsWString(piece16); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::WideToUTF8(p)); } #endif void ParamTraits>::Write(base::Pickle* m, const param_type& p) { if (p.empty()) { m->WriteData(NULL, 0); } else { m->WriteData(&p.front(), p.size()); } } bool ParamTraits>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char *data; size_t data_size = 0; if (!iter->ReadData(&data, &data_size)) return false; r->resize(data_size); if (data_size) memcpy(&r->front(), data, data_size); return true; } void ParamTraits >::Log(const param_type& p, std::string* l) { LogBytes(p, l); } void ParamTraits>::Write(base::Pickle* m, const param_type& p) { if (p.empty()) { m->WriteData(NULL, 0); } else { m->WriteData(reinterpret_cast(&p.front()), p.size()); } } bool ParamTraits>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char *data; size_t data_size = 0; if (!iter->ReadData(&data, &data_size)) return false; r->resize(data_size); if (data_size) memcpy(&r->front(), data, data_size); return true; } void ParamTraits >::Log(const param_type& p, std::string* l) { LogBytes(p, l); } void ParamTraits>::Write(base::Pickle* m, const param_type& p) { WriteParam(m, base::checked_cast(p.size())); // Cast to bool below is required because libc++'s // vector::const_reference is different from bool, and we want to avoid // writing an extra specialization of ParamTraits for it. for (size_t i = 0; i < p.size(); i++) WriteParam(m, static_cast(p[i])); } bool ParamTraits>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { size_t size; if (!iter->ReadLength(&size)) return false; r->resize(size); for (size_t i = 0; i < size; i++) { bool value; if (!ReadParam(m, iter, &value)) return false; (*r)[i] = value; } return true; } void ParamTraits >::Log(const param_type& p, std::string* l) { for (size_t i = 0; i < p.size(); ++i) { if (i != 0) l->push_back(' '); LogParam(static_cast(p[i]), l); } } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteDictValue(p.GetDict(), 0, m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadDictValue(m, iter, 0, &(r->GetDict())); } void ParamTraits::Log(const param_type& p, std::string* l) { std::string json; base::JSONWriter::Write(p, &json); l->append(json); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteDictValue(p, 0, m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadDictValue(m, iter, 0, r); } void ParamTraits::Log(const param_type& p, std::string* l) { std::string json; base::JSONWriter::Write(p, &json); l->append(json); } #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) void ParamTraits::Write(base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with // nacl_message_scanner.cc:WriteHandle(). const bool valid = p.fd >= 0; WriteParam(m, valid); if (!valid) return; if (p.auto_close) { if (!m->WriteAttachment( new internal::PlatformFileAttachment(base::ScopedFD(p.fd)))) NOTREACHED(); } else { if (!m->WriteAttachment(new internal::PlatformFileAttachment(p.fd))) NOTREACHED(); } } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { *r = base::FileDescriptor(); bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) return true; scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; if (static_cast(attachment.get())->GetType() != MessageAttachment::Type::PLATFORM_FILE) { return false; } *r = base::FileDescriptor( static_cast(attachment.get()) ->TakePlatformFile(), true); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { if (p.auto_close) { l->append(base::StringPrintf("FD(%d auto-close)", p.fd)); } else { l->append(base::StringPrintf("FD(%d)", p.fd)); } } void ParamTraits::Write(base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with // nacl_message_scanner.cc:WriteHandle(). const bool valid = p.is_valid(); WriteParam(m, valid); if (!valid) return; if (!m->WriteAttachment(new internal::PlatformFileAttachment( std::move(const_cast(p))))) { NOTREACHED(); } } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { r->reset(); bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) return true; scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; if (static_cast(attachment.get())->GetType() != MessageAttachment::Type::PLATFORM_FILE) { return false; } *r = base::ScopedFD( static_cast(attachment.get()) ->TakePlatformFile()); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::StringPrintf("ScopedFD(%d)", p.get())); } #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #if BUILDFLAG(IS_WIN) void ParamTraits::Write(base::Pickle* m, const param_type& p) { const bool valid = p.IsValid(); WriteParam(m, valid); if (!valid) return; HandleWin handle(p.Get()); WriteParam(m, handle); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { r->Close(); bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) return true; HandleWin handle; if (!ReadParam(m, iter, &handle)) return false; r->Set(handle.get_handle()); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::StringPrintf("ScopedHandle(%p)", p.Get())); } #endif // BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_FUCHSIA) void ParamTraits::Write(base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with // nacl_message_scanner.cc:WriteHandle(). const bool valid = p.is_valid(); WriteParam(m, valid); if (!valid) return; if (!m->WriteAttachment(new internal::HandleAttachmentFuchsia( std::move(const_cast(p))))) { NOTREACHED(); } } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { r->reset(); bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) return true; scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; if (static_cast(attachment.get())->GetType() != MessageAttachment::Type::FUCHSIA_HANDLE) { return false; } *r = zx::vmo(static_cast(attachment.get()) ->Take()); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append("ZirconVMO"); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with // nacl_message_scanner.cc:WriteHandle(). const bool valid = p.is_valid(); WriteParam(m, valid); if (!valid) return; if (!m->WriteAttachment(new internal::HandleAttachmentFuchsia( std::move(const_cast(p))))) { NOTREACHED(); } } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { r->reset(); bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) return true; scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; if (static_cast(attachment.get())->GetType() != MessageAttachment::Type::FUCHSIA_HANDLE) { return false; } *r = zx::channel( static_cast(attachment.get()) ->Take()); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append("ZirconChannel"); } #endif // BUILDFLAG(IS_FUCHSIA) #if BUILDFLAG(IS_ANDROID) void ParamTraits::Write( base::Pickle* m, const param_type& p) { const bool is_valid = p.is_valid(); WriteParam(m, is_valid); if (!is_valid) return; // We must keep a ref to the AHardwareBuffer alive until the receiver has // acquired its own reference. We do this by sending a message pipe handle // along with the buffer. When the receiver deserializes (or even if they // die without ever reading the message) their end of the pipe will be // closed. We will eventually detect this and release the AHB reference. mojo::MessagePipe tracking_pipe; m->WriteAttachment(new internal::MojoHandleAttachment( mojo::ScopedHandle::From(std::move(tracking_pipe.handle0)))); WriteParam(m, base::FileDescriptor(p.SerializeAsFileDescriptor().release(), true /* auto_close */)); // Pass ownership of the input handle to our tracking pipe to keep the AHB // alive long enough to be deserialized by the receiver. mojo::ScopeToMessagePipe(std::move(const_cast(p)), std::move(tracking_pipe.handle1)); } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* r) { *r = base::android::ScopedHardwareBufferHandle(); bool is_valid; if (!ReadParam(m, iter, &is_valid)) return false; if (!is_valid) return true; scoped_refptr tracking_pipe_attachment; if (!m->ReadAttachment(iter, &tracking_pipe_attachment)) return false; // We keep this alive until the AHB is safely deserialized below. When this // goes out of scope, the sender holding the other end of this pipe will treat // this handle closure as a signal that it's safe to release their AHB // keepalive ref. mojo::ScopedHandle tracking_pipe = static_cast(tracking_pipe_attachment.get()) ->TakeMojoHandle(); base::FileDescriptor descriptor; if (!ReadParam(m, iter, &descriptor)) return false; // NOTE: It is valid to deserialize an invalid FileDescriptor, so the success // of |ReadParam()| above does not imply that |descriptor| is valid. base::ScopedFD scoped_fd(descriptor.fd); if (!scoped_fd.is_valid()) return false; *r = base::android::ScopedHardwareBufferHandle::DeserializeFromFileDescriptor( std::move(scoped_fd)); return true; } void ParamTraits::Log( const param_type& p, std::string* l) { l->append(base::StringPrintf("base::android::ScopedHardwareBufferHandle(%p)", p.get())); } #endif // BUILDFLAG(IS_ANDROID) void ParamTraits::Write(base::Pickle* m, const param_type& p) { base::subtle::PlatformSharedMemoryRegion handle = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization( std::move(const_cast(p))); WriteParam(m, std::move(handle)); } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* r) { base::subtle::PlatformSharedMemoryRegion handle; if (!ReadParam(m, iter, &handle)) return false; *r = base::ReadOnlySharedMemoryRegion::Deserialize(std::move(handle)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { *l = ""; // TODO(alexilin): currently there is no way to access underlying handle // without destructing a ReadOnlySharedMemoryRegion instance. } void ParamTraits::Write(base::Pickle* m, const param_type& p) { base::subtle::PlatformSharedMemoryRegion handle = base::WritableSharedMemoryRegion::TakeHandleForSerialization( std::move(const_cast(p))); WriteParam(m, std::move(handle)); } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* r) { base::subtle::PlatformSharedMemoryRegion handle; if (!ReadParam(m, iter, &handle)) return false; *r = base::WritableSharedMemoryRegion::Deserialize(std::move(handle)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { *l = ""; // TODO(alexilin): currently there is no way to access underlying handle // without destructing a ReadOnlySharedMemoryRegion instance. } void ParamTraits::Write(base::Pickle* m, const param_type& p) { base::subtle::PlatformSharedMemoryRegion handle = base::UnsafeSharedMemoryRegion::TakeHandleForSerialization( std::move(const_cast(p))); WriteParam(m, std::move(handle)); } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* r) { base::subtle::PlatformSharedMemoryRegion handle; if (!ReadParam(m, iter, &handle)) return false; *r = base::UnsafeSharedMemoryRegion::Deserialize(std::move(handle)); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { *l = ""; // TODO(alexilin): currently there is no way to access underlying handle // without destructing a ReadOnlySharedMemoryRegion instance. } void ParamTraits::Write( base::Pickle* m, const param_type& p) { // This serialization must be kept in sync with // nacl_message_scanner.cc::WriteHandle(). const bool valid = p.IsValid(); WriteParam(m, valid); if (!valid) return; WriteParam(m, p.GetMode()); WriteParam(m, static_cast(p.GetSize())); WriteParam(m, p.GetGUID()); #if BUILDFLAG(IS_WIN) base::win::ScopedHandle h = const_cast(p).PassPlatformHandle(); HandleWin handle_win(h.Get()); WriteParam(m, handle_win); #elif BUILDFLAG(IS_FUCHSIA) zx::vmo vmo = const_cast(p).PassPlatformHandle(); WriteParam(m, vmo); #elif BUILDFLAG(IS_APPLE) base::mac::ScopedMachSendRight h = const_cast(p).PassPlatformHandle(); MachPortMac mach_port_mac(h.get()); WriteParam(m, mach_port_mac); #elif BUILDFLAG(IS_ANDROID) m->WriteAttachment(new internal::PlatformFileAttachment( base::ScopedFD(const_cast(p).PassPlatformHandle()))); #elif BUILDFLAG(IS_POSIX) base::subtle::ScopedFDPair h = const_cast(p).PassPlatformHandle(); m->WriteAttachment(new internal::PlatformFileAttachment(std::move(h.fd))); if (p.GetMode() == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { m->WriteAttachment( new internal::PlatformFileAttachment(std::move(h.readonly_fd))); } #endif } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* r) { bool valid; if (!ReadParam(m, iter, &valid)) return false; if (!valid) { *r = base::subtle::PlatformSharedMemoryRegion(); return true; } base::subtle::PlatformSharedMemoryRegion::Mode mode; uint64_t shm_size; base::UnguessableToken guid; if (!ReadParam(m, iter, &mode) || !ReadParam(m, iter, &shm_size) || !base::IsValueInRangeForNumericType(shm_size) || !ReadParam(m, iter, &guid)) { return false; } size_t size = static_cast(shm_size); #if BUILDFLAG(IS_WIN) HandleWin handle_win; if (!ReadParam(m, iter, &handle_win)) return false; *r = base::subtle::PlatformSharedMemoryRegion::Take( base::win::ScopedHandle(handle_win.get_handle()), mode, size, guid); #elif BUILDFLAG(IS_FUCHSIA) zx::vmo vmo; if (!ReadParam(m, iter, &vmo)) return false; *r = base::subtle::PlatformSharedMemoryRegion::Take(std::move(vmo), mode, size, guid); #elif BUILDFLAG(IS_APPLE) MachPortMac mach_port_mac; if (!ReadParam(m, iter, &mach_port_mac)) return false; *r = base::subtle::PlatformSharedMemoryRegion::Take( base::mac::ScopedMachSendRight(mach_port_mac.get_mach_port()), mode, size, guid); #elif BUILDFLAG(IS_POSIX) scoped_refptr attachment; if (!m->ReadAttachment(iter, &attachment)) return false; if (static_cast(attachment.get())->GetType() != MessageAttachment::Type::PLATFORM_FILE) { return false; } #if BUILDFLAG(IS_ANDROID) *r = base::subtle::PlatformSharedMemoryRegion::Take( base::ScopedFD( static_cast(attachment.get()) ->TakePlatformFile()), mode, size, guid); #else scoped_refptr readonly_attachment; if (mode == base::subtle::PlatformSharedMemoryRegion::Mode::kWritable) { if (!m->ReadAttachment(iter, &readonly_attachment)) return false; if (static_cast(readonly_attachment.get())->GetType() != MessageAttachment::Type::PLATFORM_FILE) { return false; } } *r = base::subtle::PlatformSharedMemoryRegion::Take( base::subtle::ScopedFDPair( base::ScopedFD( static_cast(attachment.get()) ->TakePlatformFile()), readonly_attachment ? base::ScopedFD(static_cast( readonly_attachment.get()) ->TakePlatformFile()) : base::ScopedFD()), mode, size, guid); #endif // BUILDFLAG(IS_ANDROID) #endif return true; } void ParamTraits::Log( const param_type& p, std::string* l) { #if BUILDFLAG(IS_FUCHSIA) l->append("Handle: "); LogParam(p.GetPlatformHandle()->get(), l); #elif BUILDFLAG(IS_WIN) l->append("Handle: "); LogParam(p.GetPlatformHandle(), l); #elif BUILDFLAG(IS_APPLE) l->append("Mach port: "); LogParam(p.GetPlatformHandle(), l); #elif BUILDFLAG(IS_ANDROID) l->append("FD: "); LogParam(p.GetPlatformHandle(), l); #elif BUILDFLAG(IS_POSIX) base::subtle::FDPair h = p.GetPlatformHandle(); l->append("FD: "); LogParam(h.fd, l); l->append("Read-only FD: "); LogParam(h.readonly_fd, l); #endif l->append("Mode: "); LogParam(p.GetMode(), l); l->append("size: "); LogParam(static_cast(p.GetSize()), l); l->append("GUID: "); LogParam(p.GetGUID(), l); } void ParamTraits::Write( base::Pickle* m, const param_type& value) { DCHECK(static_cast(value) >= 0 && static_cast(value) <= static_cast(param_type::kMaxValue)); m->WriteInt(static_cast(value)); } bool ParamTraits::Read( const base::Pickle* m, base::PickleIterator* iter, param_type* p) { int value; if (!iter->ReadInt(&value)) return false; if (!(static_cast(value) >= 0 && static_cast(value) <= static_cast(param_type::kMaxValue))) { return false; } *p = static_cast(value); return true; } void ParamTraits::Log( const param_type& p, std::string* l) { LogParam(static_cast(p), l); } #if BUILDFLAG(IS_WIN) void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteBool(p.IsValid()); if (p.IsValid()) { HandleWin handle_win(p.GetHandle()); ParamTraits::Write(m, handle_win); ::CloseHandle(p.GetHandle()); } } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { bool is_valid; if (!iter->ReadBool(&is_valid)) return false; if (!is_valid) { *r = PlatformFileForTransit(); return true; } HandleWin handle_win; if (!ParamTraits::Read(m, iter, &handle_win)) return false; *r = PlatformFileForTransit(handle_win.get_handle()); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { LogParam(p.GetHandle(), l); } #endif // BUILDFLAG(IS_WIN) void ParamTraits::Write(base::Pickle* m, const param_type& p) { p.WriteToPickle(m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return r->ReadFromPickle(iter); } void ParamTraits::Log(const param_type& p, std::string* l) { ParamTraits::Log(p.value(), l); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteListValue(p.GetList(), 0, m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadListValue(m, iter, 0, &(r->GetList())); } void ParamTraits::Log(const param_type& p, std::string* l) { std::string json; base::JSONWriter::Write(p, &json); l->append(json); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteListValue(p, 0, m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadListValue(m, iter, 0, r); } void ParamTraits::Log(const param_type& p, std::string* l) { std::string json; base::JSONWriter::Write(p, &json); l->append(json); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteValue(p, 0, m); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadValue(m, iter, 0, r); } void ParamTraits::Log(const param_type& p, std::string* l) { std::string json; base::JSONWriter::Write(p, &json); l->append(json); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteParam(m, p.size); WriteParam(m, p.is_directory); WriteParam(m, p.last_modified.ToDoubleT()); WriteParam(m, p.last_accessed.ToDoubleT()); WriteParam(m, p.creation_time.ToDoubleT()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* p) { double last_modified, last_accessed, creation_time; if (!ReadParam(m, iter, &p->size) || !ReadParam(m, iter, &p->is_directory) || !ReadParam(m, iter, &last_modified) || !ReadParam(m, iter, &last_accessed) || !ReadParam(m, iter, &creation_time)) return false; p->last_modified = base::Time::FromDoubleT(last_modified); p->last_accessed = base::Time::FromDoubleT(last_accessed); p->creation_time = base::Time::FromDoubleT(creation_time); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append("("); LogParam(p.size, l); l->append(","); LogParam(p.is_directory, l); l->append(","); LogParam(p.last_modified.ToDoubleT(), l); l->append(","); LogParam(p.last_accessed.ToDoubleT(), l); l->append(","); LogParam(p.creation_time.ToDoubleT(), l); l->append(")"); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { ParamTraits::Write(m, p.ToInternalValue()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int64_t value; if (!ParamTraits::Read(m, iter, &value)) return false; *r = base::Time::FromInternalValue(value); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { ParamTraits::Log(p.ToInternalValue(), l); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { ParamTraits::Write(m, p.ToInternalValue()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int64_t value; bool ret = ParamTraits::Read(m, iter, &value); if (ret) *r = base::TimeDelta::FromInternalValue(value); return ret; } void ParamTraits::Log(const param_type& p, std::string* l) { ParamTraits::Log(p.ToInternalValue(), l); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { ParamTraits::Write(m, p.ToInternalValue()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int64_t value; bool ret = ParamTraits::Read(m, iter, &value); if (ret) *r = base::TimeTicks::FromInternalValue(value); return ret; } void ParamTraits::Log(const param_type& p, std::string* l) { ParamTraits::Log(p.ToInternalValue(), l); } // If base::UnguessableToken is no longer 128 bits, the IPC serialization logic // below should be updated. static_assert(sizeof(base::UnguessableToken) == 2 * sizeof(uint64_t), "base::UnguessableToken should be of size 2 * sizeof(uint64_t)."); void ParamTraits::Write(base::Pickle* m, const param_type& p) { DCHECK(!p.is_empty()); ParamTraits::Write(m, p.GetHighForSerialization()); ParamTraits::Write(m, p.GetLowForSerialization()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { uint64_t high, low; if (!ParamTraits::Read(m, iter, &high) || !ParamTraits::Read(m, iter, &low)) return false; // Receiving a zeroed UnguessableToken is a security issue. if (high == 0 && low == 0) return false; *r = base::UnguessableToken::Deserialize(high, low); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(p.ToString()); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { #if BUILDFLAG(IS_NACL) WriteParam(m, p.socket); #else WriteParam(m, p.mojo_handle); #endif } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { #if BUILDFLAG(IS_NACL) return ReadParam(m, iter, &r->socket); #else return ReadParam(m, iter, &r->mojo_handle); #endif } void ParamTraits::Log(const param_type& p, std::string* l) { l->append("ChannelHandle("); #if BUILDFLAG(IS_NACL) ParamTraits::Log(p.socket, l); #else LogParam(p.mojo_handle, l); #endif l->append(")"); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { WriteParam(m, p.channel); WriteParam(m, p.routing_id); WriteParam(m, p.type); WriteParam(m, p.flags); WriteParam(m, p.sent); WriteParam(m, p.receive); WriteParam(m, p.dispatch); WriteParam(m, p.message_name); WriteParam(m, p.params); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { return ReadParam(m, iter, &r->channel) && ReadParam(m, iter, &r->routing_id) && ReadParam(m, iter, &r->type) && ReadParam(m, iter, &r->flags) && ReadParam(m, iter, &r->sent) && ReadParam(m, iter, &r->receive) && ReadParam(m, iter, &r->dispatch) && ReadParam(m, iter, &r->message_name) && ReadParam(m, iter, &r->params); } void ParamTraits::Log(const param_type& p, std::string* l) { // Doesn't make sense to implement this! } void ParamTraits::Write(base::Pickle* m, const Message& p) { #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) // We don't serialize the file descriptors in the nested message, so there // better not be any. DCHECK(!p.HasAttachments()); #endif // Don't just write out the message. This is used to send messages between // NaCl (Posix environment) and the browser (could be on Windows). The message // header formats differ between these systems (so does handle sharing, but // we already asserted we don't have any handles). So just write out the // parts of the header we use. // // Be careful also to use only explicitly-sized types. The NaCl environment // could be 64-bit and the host browser could be 32-bits. The nested message // may or may not be safe to send between 32-bit and 64-bit systems, but we // leave that up to the code sending the message to ensure. m->WriteUInt32(static_cast(p.routing_id())); m->WriteUInt32(p.type()); m->WriteUInt32(p.flags()); m->WriteData(p.payload(), p.payload_size()); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, Message* r) { uint32_t routing_id, type, flags; if (!iter->ReadUInt32(&routing_id) || !iter->ReadUInt32(&type) || !iter->ReadUInt32(&flags)) return false; size_t payload_size; const char* payload; if (!iter->ReadData(&payload, &payload_size)) return false; r->SetHeaderValues(static_cast(routing_id), type, flags); r->WriteBytes(payload, payload_size); return true; } void ParamTraits::Log(const Message& p, std::string* l) { l->append(""); } #if BUILDFLAG(IS_WIN) // Note that HWNDs/HANDLE/HCURSOR/HACCEL etc are always 32 bits, even on 64 // bit systems. That's why we use the Windows macros to convert to 32 bits. void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteInt(HandleToLong(p)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int32_t temp; if (!iter->ReadInt(&temp)) return false; *r = LongToHandle(temp); return true; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(base::StringPrintf("0x%p", p)); } void ParamTraits::Write(base::Pickle* m, const param_type& p) { m->WriteData(reinterpret_cast(&p), sizeof(MSG)); } bool ParamTraits::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { const char *data; size_t data_size = 0; bool result = iter->ReadData(&data, &data_size); if (result && data_size == sizeof(MSG)) { memcpy(r, data, sizeof(MSG)); } else { result = false; NOTREACHED(); } return result; } void ParamTraits::Log(const param_type& p, std::string* l) { l->append(""); } #endif // BUILDFLAG(IS_WIN) } // namespace IPC