Files
roytam1 f632bc6ab3 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1267186 - Split lookup of WebIDL DOM class names from lookup of DOMCI DOM class names. r=bz. (1cb4c3698e)
- Bug 1206637: P1. Add MediaPrefs convenience class. r=cpearce (d352b9ca0b)
- Bug 1254378 - Make new 'speech-synth-started' component service category. r=smaug (ece851540b)
- Bug 1206637: P2. Replace all cached preferences with MediaPrefs ones. r=cpearce (1492083f62)
- Bug 1216407 - Use mozilla/Endian.h for NfcService. r=yoshi (b2a508186d)
- Bug 1237493 - [NFC] Separate Gecko and Gonk layers for accessing Androids properties. r=yoshi (062e468bed)
- Bug 1272135, part 1 - Fix leading tab usage in ipc/chromium. r=billm (cf29df6977)
- Bug 1272135, part 2 - Delete two weird modelines in ipc/chromium. r=billm (da4cdafc7c)
- Bug 1272135, part 3 - Fix mode lines in ipc/chromium/. r=billm (040b11738e)
- Bug 1273307 - Remove copy constructor/assignment operator for Message/Pickle (r=froydnj) (b53e6d3470)
- Bug 1262671 - Remove unused TrimWriteData (r=froydnj) (8fbcefde63)
- Bug 1267438 - Group ScrollMetadata's optional clip rect and mask layer index into a LayerClip structure. r=mstange (dcd01e3bce)
- Bug 1267438 - Use IntersectMaybeRects() in Layer::GetCombinedClipRect(). r=mstange (5ec2d68aac)
- Bug 1262937 - part 1 - don't include the protocol name in Clone error messages; r=jld (3e23610fb1)
- Bug 1191452 - Limit IPDL-generated Move()s to Recv methods. r=billm (991cc733b6)
- Bug 1262937 - part 2 - don't include the message name when complaining about handler failure; r=jld (b8abbc5c8b)
- Bug 1262937 - part 3 - move quoting out of checkedRead; r=jld (16d36ae02a)
- Bug 1262937 - part 4 - publically inherit from MessageListener in IProtocol; r=jld (b89aaa7347)
- Bug 1262937 - part 5 - factor out actor reading code to a common base class; r=jld (702388bce7)
- Bug 1262937 - part 7 - factor out union type deserialization errors; r=jld (dcdf428efc)
- Bug 1262937 - part 6 - enable custom error message for ipdl.py's checkedRead; r=jld (5cf11d5d24)
- Bug 1262937 - part 8 - factor out array length deserialization errors; r=jld (d1149cc120)
- Bug 1271601: If a child process fails to duplicate a TransportDescriptor pipe handle then send it anyway and get the target to duplicate. r=gabor (d578cd0c85)
- Bug 1270247 - Crash in ParamTraits<mozilla::net::NetAddr>::Write if the family is unknown. r=hurley (3ed49495c0)
- Bug 1267474 - cache-control: immutable tests 3/3 r=mayhemer (ba0bc6e71a)
- bug 1188100 - fold PSM's test_client_cert.js into necko's test_tls_server.js r=mcmanus (a2b93abbbb)
- Bug 669259 - Expose original header received from a peer. r=mcmanus (cf53cad7ac)
- Bug 386743 - Set default event bubbling/cancelable flags in the WidgetEvent constructor. r=smaug (1c68cfffd2)
- Bug 1259661 part.1 Rename WidgetMouseEvent::reasonType to WidgetMouseEvent::Reason r=smaug (0edd107b56)
- Bug 1259661 part.2 Rename WidgetMouseEvent::context to WidgetMouseEvent::ContextMenuTrigger r=smaug (0049757099)
- Bug 1259661 part.3 Rename WidgetMouseEvent::exitType to WidgetMouseEvent::ExitFrom r=smaug (5ffd3f41b1)
- Bug 1259661 part.4 Rename WidgetMouseEvent::reason to WidgetMouseEvent::mReason r=smaug (5605ce8fd0)
- Bug 1259661 part.5 Rename WidgetMouseEvent::context to WidgetMouseEvent::mContextMenuTrigger r=smaug (4dd92f0066)
- Bug 1259661 part.6 Rename WidgetMouseEvent::exit to WidgetMouseEvent::mExitFrom r=smaug (c46f5fe1bc)
- Bug 1259661 part.7 Get rid of WidgetMouseEvent::acceptActivation because of unused r=smaug (4867aaf1a7)
- Bug 1259661 part.8 Rename WidgetMouseEvent::ignoreRootScrollFrame to WidgetMouseEvent::mIgnoreRootScrollFrame r=smaug (c829c446fd)
- Bug 1259661 part.9 Rename WidgetMouseEvent::clickCount to WidgetMouseEvent::mClickCount r=smaug (2fa015c006)
- Bug 1259661 part.10 Clean up some nits of WidgetMouseEvent definition r=smaug (58d3a0cb2c)
- Bug 1179346 - Add strings and accesskeys for Microsoft Edge. r=mak (b97f5e7ab4)
- Bug 931445 part 1 - Remove unused code from pointerlock_utils.js. r=smaug (e1b9c15569)
- Bug 931445 part 2 - Use util code from fullscreen tests to improve robustness of pointerlock tests. r=smaug (3c7b47751e)
- Bug 931445 part 3 - Reset synth centering pointer when unlocking pointer. r=smaug (e272fec9c7)
- Bug 1263389 NativeKey should initialize WidgetKeyboardEvent::mKeyValue of WM_KEYDOWN of VK_PACKET with following char message r=m_kato (e56a31bd4b)
- Bug 1261880 NativeKey should decide printable KeyboardEvent.key value of keydown and keypress events with following WM_CHAR message of WM_KEYDOWN r=m_kato (7208001852)
- Bug 1254755 part.1 Rename WidgetKeyboardEvent::keyCode to WidgetKeyboardEvent::mKeyCode r=smaug (97ebefa314)
- put back some XP/2003 code, as well as crash/debug (73b3147e58)
- Bug 1259679 - Space key shouldn't work as a space key if it's assigned to a function key. r=masayuki (db422c248b)
- Bug 1101975, handle access keys in content process before menus, r=masayuki (760213b62e)
- Bug 1254755 part.2 Rename WidgetKeyboardEvent::charCode to WidgetKeyboardEvent::mCharCode r=smaug (6cef6114a5)
- Bug 1254755 part.3 Rename WidgetKeyboardEvent::alternativeCharCodes to WidgetKeyboardEvent::mAlternativeCharCodes r=smaug (9d23a113fd)
- Bug 1254755 part.4 Rename WidgetKeyboardEvent::location to WidgetKeyboardEvent::mLocation r=smaug (f44e0212a6)
- Bug 1254755 part.5 Rename WidgetKeyboardEvent::isChar to WidgetKeyboardEvent::mIsChar r=smaug (a713d9903c)
- Bug 1254755 part.6 Reorder the members of WidgetKeyboardEvent for reducing its instance size r=smaug (39a14a7d4f)
- Bug 1262671 - void** -> PickleIterator (r=froydnj) (716a88b499)
2024-10-08 21:42:19 +08:00

499 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Shmem.h"
#include "ProtocolUtils.h"
#include "SharedMemoryBasic.h"
#include "mozilla/unused.h"
namespace mozilla {
namespace ipc {
class ShmemCreated : public IPC::Message
{
private:
typedef Shmem::id_t id_t;
public:
ShmemCreated(int32_t routingId,
id_t aIPDLId,
size_t aSize,
SharedMemory::SharedMemoryType aType) :
IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, PRIORITY_NORMAL)
{
IPC::WriteParam(this, aIPDLId);
IPC::WriteParam(this, aSize);
IPC::WriteParam(this, int32_t(aType));
}
static bool
ReadInfo(const Message* msg, PickleIterator* iter,
id_t* aIPDLId,
size_t* aSize,
SharedMemory::SharedMemoryType* aType)
{
if (!IPC::ReadParam(msg, iter, aIPDLId) ||
!IPC::ReadParam(msg, iter, aSize) ||
!IPC::ReadParam(msg, iter, reinterpret_cast<int32_t*>(aType)))
return false;
return true;
}
void Log(const std::string& aPrefix,
FILE* aOutf) const
{
fputs("(special ShmemCreated msg)", aOutf);
}
};
class ShmemDestroyed : public IPC::Message
{
private:
typedef Shmem::id_t id_t;
public:
ShmemDestroyed(int32_t routingId,
id_t aIPDLId) :
IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE, PRIORITY_NORMAL)
{
IPC::WriteParam(this, aIPDLId);
}
};
static SharedMemory*
NewSegment(SharedMemory::SharedMemoryType aType)
{
if (SharedMemory::TYPE_BASIC == aType) {
return new SharedMemoryBasic;
} else {
NS_ERROR("unknown Shmem type");
return nullptr;
}
}
static already_AddRefed<SharedMemory>
CreateSegment(SharedMemory::SharedMemoryType aType, size_t aNBytes, size_t aExtraSize)
{
RefPtr<SharedMemory> segment = NewSegment(aType);
if (!segment) {
return nullptr;
}
size_t size = SharedMemory::PageAlignedSize(aNBytes + aExtraSize);
if (!segment->Create(size) || !segment->Map(size)) {
return nullptr;
}
return segment.forget();
}
static already_AddRefed<SharedMemory>
ReadSegment(const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes, size_t aExtraSize)
{
if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
NS_ERROR("expected 'shmem created' message");
return nullptr;
}
SharedMemory::SharedMemoryType type;
PickleIterator iter(aDescriptor);
if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, aNBytes, &type)) {
return nullptr;
}
RefPtr<SharedMemory> segment = NewSegment(type);
if (!segment) {
return nullptr;
}
if (!segment->ReadHandle(&aDescriptor, &iter)) {
NS_ERROR("trying to open invalid handle");
return nullptr;
}
aDescriptor.EndRead(iter);
size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize);
if (!segment->Map(size)) {
return nullptr;
}
// close the handle to the segment after it is mapped
segment->CloseHandle();
return segment.forget();
}
static void
DestroySegment(SharedMemory* aSegment)
{
// the SharedMemory dtor closes and unmaps the actual OS shmem segment
if (aSegment) {
aSegment->Release();
}
}
#if defined(DEBUG)
static const char sMagic[] =
"This little piggy went to market.\n"
"This little piggy stayed at home.\n"
"This little piggy has roast beef,\n"
"This little piggy had none.\n"
"And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
struct Header {
// Don't use size_t or bool here because their size depends on the
// architecture.
uint32_t mSize;
uint32_t mUnsafe;
char mMagic[sizeof(sMagic)];
};
static void
GetSections(Shmem::SharedMemory* aSegment,
Header** aHeader,
char** aFrontSentinel,
char** aData,
char** aBackSentinel)
{
MOZ_ASSERT(aSegment && aFrontSentinel && aData && aBackSentinel,
"null param(s)");
*aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
MOZ_ASSERT(*aFrontSentinel, "null memory()");
*aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
size_t pageSize = Shmem::SharedMemory::SystemPageSize();
*aData = *aFrontSentinel + pageSize;
*aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
}
static Header*
GetHeader(Shmem::SharedMemory* aSegment)
{
Header* header;
char* dontcare;
GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
return header;
}
static void
Protect(SharedMemory* aSegment)
{
MOZ_ASSERT(aSegment, "null segment");
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
aSegment->Size(),
RightsNone);
}
static void
Unprotect(SharedMemory* aSegment)
{
MOZ_ASSERT(aSegment, "null segment");
aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
aSegment->Size(),
RightsRead | RightsWrite);
}
//
// In debug builds, we specially allocate shmem segments. The layout
// is as follows
//
// Page 0: "front sentinel"
// size of mapping
// magic bytes
// Page 1 through n-1:
// user data
// Page n: "back sentinel"
// [nothing]
//
// The mapping can be in one of the following states, wrt to the
// current process.
//
// State "unmapped": all pages are mapped with no access rights.
//
// State "mapping": all pages are mapped with read/write access.
//
// State "mapped": the front and back sentinels are mapped with no
// access rights, and all the other pages are mapped with
// read/write access.
//
// When a SharedMemory segment is first allocated, it starts out in
// the "mapping" state for the process that allocates the segment, and
// in the "unmapped" state for the other process. The allocating
// process will then create a Shmem, which takes the segment into the
// "mapped" state, where it can be accessed by clients.
//
// When a Shmem is sent to another process in an IPDL message, the
// segment transitions into the "unmapped" state for the sending
// process, and into the "mapping" state for the receiving process.
// The receiving process will then create a Shmem from the underlying
// segment, and take the segment into the "mapped" state.
//
// In the "mapping" state, we use the front sentinel to verify the
// integrity of the shmem segment. If valid, it has a size_t
// containing the number of bytes the user allocated followed by the
// magic bytes above.
//
// In the "mapped" state, the front and back sentinels have no access
// rights. They act as guards against buffer overflows and underflows
// in client code; if clients touch a sentinel, they die with SIGSEGV.
//
// The "unmapped" state is used to enforce single-owner semantics of
// the shmem segment. If a process other than the current owner tries
// to touch the segment, it dies with SIGSEGV.
//
Shmem::Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
SharedMemory* aSegment, id_t aId) :
mSegment(aSegment),
mData(nullptr),
mSize(0)
{
MOZ_ASSERT(mSegment, "null segment");
MOZ_ASSERT(aId != 0, "invalid ID");
Unprotect(mSegment);
Header* header;
char* frontSentinel;
char* data;
char* backSentinel;
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
// do a quick validity check to avoid weird-looking crashes in libc
char check = *frontSentinel;
(void)check;
MOZ_ASSERT(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
"invalid segment");
mSize = static_cast<size_t>(header->mSize);
size_t pageSize = SharedMemory::SystemPageSize();
// transition into the "mapped" state by protecting the front and
// back sentinels (which guard against buffer under/overflows)
mSegment->Protect(frontSentinel, pageSize, RightsNone);
mSegment->Protect(backSentinel, pageSize, RightsNone);
// don't set these until we know they're valid
mData = data;
mId = aId;
}
void
Shmem::AssertInvariants() const
{
MOZ_ASSERT(mSegment, "null segment");
MOZ_ASSERT(mData, "null data pointer");
MOZ_ASSERT(mSize > 0, "invalid size");
// if the segment isn't owned by the current process, these will
// trigger SIGSEGV
char checkMappingFront = *reinterpret_cast<char*>(mData);
char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
// avoid "unused" warnings for these variables:
Unused << checkMappingFront;
Unused << checkMappingBack;
}
void
Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
{
AssertInvariants();
size_t pageSize = SharedMemory::SystemPageSize();
Header* header = GetHeader(mSegment);
// Open this up for reading temporarily
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
if (!header->mUnsafe) {
Protect(mSegment);
} else {
mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
}
}
// static
already_AddRefed<Shmem::SharedMemory>
Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
size_t aNBytes,
SharedMemoryType aType,
bool aUnsafe,
bool aProtect)
{
NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
MOZ_ASSERT(!aProtect || !aUnsafe, "protect => !unsafe");
size_t pageSize = SharedMemory::SystemPageSize();
// |2*pageSize| is for the front and back sentinel
RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, 2*pageSize);
if (!segment) {
return nullptr;
}
Header* header;
char *frontSentinel;
char *data;
char *backSentinel;
GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
// initialize the segment with Shmem-internal information
// NB: this can't be a static assert because technically pageSize
// isn't known at compile time, event though in practice it's always
// going to be 4KiB
MOZ_ASSERT(sizeof(Header) <= pageSize,
"Shmem::Header has gotten too big");
memcpy(header->mMagic, sMagic, sizeof(sMagic));
header->mSize = static_cast<uint32_t>(aNBytes);
header->mUnsafe = aUnsafe;
if (aProtect)
Protect(segment);
return segment.forget();
}
// static
already_AddRefed<Shmem::SharedMemory>
Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
const IPC::Message& aDescriptor,
id_t* aId,
bool aProtect)
{
size_t size;
size_t pageSize = SharedMemory::SystemPageSize();
// |2*pageSize| is for the front and back sentinels
RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, 2*pageSize);
if (!segment) {
return nullptr;
}
Header* header = GetHeader(segment);
if (size != header->mSize) {
// Deallocation should zero out the header, so check for that.
if (header->mSize || header->mUnsafe || header->mMagic[0] ||
memcmp(header->mMagic, &header->mMagic[1], sizeof(header->mMagic)-1)) {
NS_ERROR("Wrong size for this Shmem!");
} else {
NS_WARNING("Shmem was deallocated");
}
return nullptr;
}
// The caller of this function may not know whether the segment is
// unsafe or not
if (!header->mUnsafe && aProtect)
Protect(segment);
return segment.forget();
}
// static
void
Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
SharedMemory* aSegment)
{
if (!aSegment)
return;
size_t pageSize = SharedMemory::SystemPageSize();
Header* header;
char *frontSentinel;
char *data;
char *backSentinel;
GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
memset(header->mMagic, 0, sizeof(sMagic));
header->mSize = 0;
header->mUnsafe = false; // make it "safe" so as to catch errors
DestroySegment(aSegment);
}
#else // !defined(DEBUG)
// static
already_AddRefed<Shmem::SharedMemory>
Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
size_t aNBytes,
SharedMemoryType aType,
bool /*unused*/,
bool /*unused*/)
{
RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, sizeof(uint32_t));
if (!segment) {
return nullptr;
}
*PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
return segment.forget();
}
// static
already_AddRefed<Shmem::SharedMemory>
Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
const IPC::Message& aDescriptor,
id_t* aId,
bool /*unused*/)
{
size_t size;
RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, sizeof(uint32_t));
if (!segment) {
return nullptr;
}
// this is the only validity check done in non-DEBUG builds
if (size != static_cast<size_t>(*PtrToSize(segment))) {
return nullptr;
}
return segment.forget();
}
// static
void
Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
SharedMemory* aSegment)
{
DestroySegment(aSegment);
}
#endif // if defined(DEBUG)
IPC::Message*
Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
base::ProcessId aTargetPid,
int32_t routingId)
{
AssertInvariants();
IPC::Message *msg = new ShmemCreated(routingId, mId, mSize, mSegment->Type());
if (!mSegment->ShareHandle(aTargetPid, msg)) {
return nullptr;
}
// close the handle to the segment after it is shared
mSegment->CloseHandle();
return msg;
}
IPC::Message*
Shmem::UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
base::ProcessId aTargetPid,
int32_t routingId)
{
AssertInvariants();
return new ShmemDestroyed(routingId, mId);
}
} // namespace ipc
} // namespace mozilla