diff --git a/.clang-format-ignore b/.clang-format-ignore index 881d824e7d24..89f4a0d65833 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -96,7 +96,9 @@ gfx/angle/.* gfx/cairo/.* gfx/graphite2/.* gfx/harfbuzz/.* -gfx/ots/.* +gfx/ots/src/.* +gfx/ots/include/.* +gfx/ots/tests/.* gfx/qcms/.* gfx/sfntly/.* gfx/skia/.* @@ -167,7 +169,8 @@ modules/fdlibm/.* modules/freetype2/.* modules/libbz2/.* modules/pdfium/.* -modules/woff2/.* +modules/woff2/include/.* +modules/woff2/src/.* modules/xz-embedded/.* modules/zlib/.* mozglue/misc/decimal/.* diff --git a/config/recurse.mk b/config/recurse.mk index 4fd4bd57d67f..5e109e5172f9 100644 --- a/config/recurse.mk +++ b/config/recurse.mk @@ -217,7 +217,7 @@ endif ifdef MOZ_USING_WASM_SANDBOXING security/rlbox/target-objects: config/external/wasm2c_sandbox_compiler/host security/rlbox/target: security/rlbox/target-objects -dom/media/ogg/target-objects extensions/spellcheck/hunspell/glue/target-objects gfx/thebes/target-objects parser/expat/target-objects parser/htmlparser/target-objects: security/rlbox/target-objects +dom/media/ogg/target-objects extensions/spellcheck/hunspell/glue/target-objects gfx/thebes/target-objects parser/expat/target-objects parser/htmlparser/target-objects gfx/ots/src/target-objects: security/rlbox/target-objects endif # Most things are built during compile (target/host), but some things happen during export diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla index a5320be46962..15243ab4d3f6 100644 --- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -10,3 +10,4 @@ Additional files: README.mozilla, src/moz.build Additional patch: ots-visibility.patch (bug 711079). Additional patch: ots-lz4.patch +Additional patch: ots-rlbox.patch (bug 1732201). diff --git a/gfx/ots/RLBoxWOFF2Host.cpp b/gfx/ots/RLBoxWOFF2Host.cpp new file mode 100644 index 000000000000..56415fa48833 --- /dev/null +++ b/gfx/ots/RLBoxWOFF2Host.cpp @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "RLBoxWOFF2Host.h" +#include "nsPrintfCString.h" +#include "nsThreadUtils.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/RLBoxUtils.h" +#include "mozilla/ScopeExit.h" + +using namespace rlbox; +using namespace mozilla; + +tainted_woff2 RLBoxBrotliDecoderDecompressCallback( + rlbox_sandbox_woff2& aSandbox, tainted_woff2 aEncodedSize, + tainted_woff2 aEncodedBuffer, + tainted_woff2 aDecodedSize, + tainted_woff2 aDecodedBuffer) { + if (!aEncodedBuffer || !aDecodedSize || !aDecodedBuffer) { + return BROTLI_DECODER_RESULT_ERROR; + } + + // We don't create temporary buffers for brotli to operate on. Instead we + // pass a pointer to the in (encoded) and out (decoded) buffers. We check + // (specifically, unverified_safe_pointer checks) that the buffers are within + // the sandbox boundary (for the given sizes). + + size_t encodedSize = + aEncodedSize.unverified_safe_because("Any size within sandbox is ok."); + const uint8_t* encodedBuffer = reinterpret_cast( + aEncodedBuffer.unverified_safe_pointer_because( + encodedSize, "Pointer fits within sandbox")); + + size_t decodedSize = + (*aDecodedSize).unverified_safe_because("Any size within sandbox is ok."); + uint8_t* decodedBuffer = + reinterpret_cast(aDecodedBuffer.unverified_safe_pointer_because( + decodedSize, "Pointer fits within sandbox")); + + BrotliDecoderResult res = BrotliDecoderDecompress( + encodedSize, encodedBuffer, &decodedSize, decodedBuffer); + + *aDecodedSize = decodedSize; + + return res; +} + +UniquePtr RLBoxWOFF2SandboxPool::CreateSandboxData() { + // Create woff2 sandbox + auto sandbox = MakeUnique(); + +#ifdef MOZ_WASM_SANDBOXING_WOFF2 + bool createOK = sandbox->create_sandbox(/* infallible = */ false); +#else + bool createOK = sandbox->create_sandbox(); +#endif + NS_ENSURE_TRUE(createOK, nullptr); + + UniquePtr sbxData = + MakeUnique(std::move(sandbox)); + + // Register brotli callback + sbxData->mDecompressCallback = sbxData->Sandbox()->register_callback( + RLBoxBrotliDecoderDecompressCallback); + sbxData->Sandbox()->invoke_sandbox_function(RegisterWOFF2Callback, + sbxData->mDecompressCallback); + + return sbxData; +} + +StaticRefPtr RLBoxWOFF2SandboxPool::sSingleton; + +void RLBoxWOFF2SandboxPool::Initalize(size_t aDelaySeconds) { + AssertIsOnMainThread(); + RLBoxWOFF2SandboxPool::sSingleton = new RLBoxWOFF2SandboxPool(aDelaySeconds); + ClearOnShutdown(&RLBoxWOFF2SandboxPool::sSingleton); +} + +RLBoxWOFF2SandboxData::RLBoxWOFF2SandboxData( + mozilla::UniquePtr aSandbox) + : mSandbox(std::move(aSandbox)) { + MOZ_COUNT_CTOR(RLBoxWOFF2SandboxData); +} + +RLBoxWOFF2SandboxData::~RLBoxWOFF2SandboxData() { + MOZ_ASSERT(mSandbox); + mDecompressCallback.unregister(); + mSandbox->destroy_sandbox(); + MOZ_COUNT_DTOR(RLBoxWOFF2SandboxData); +} + +template +using TransferBufferToWOFF2 = + mozilla::RLBoxTransferBufferToSandbox; +template +using WOFF2Alloc = mozilla::RLBoxAllocateInSandbox; + +bool RLBoxProcessWOFF2(ots::FontFile* aHeader, ots::OTSStream* aOutput, + const uint8_t* aData, size_t aLength, uint32_t aIndex, + ProcessTTCFunc* aProcessTTC, + ProcessTTFFunc* aProcessTTF) { + MOZ_ASSERT(aProcessTTC); + MOZ_ASSERT(aProcessTTF); + + // We index into aData before processing it (very end of this function). Our + // validator ensures that the untrusted size is greater than aLength, so we + // just need to conservatively ensure that aLength is greater than the highest + // index (7). + NS_ENSURE_TRUE(aLength >= 8, false); + + auto sandboxPoolData = RLBoxWOFF2SandboxPool::sSingleton->PopOrCreate(); + NS_ENSURE_TRUE(sandboxPoolData, false); + + const auto* sandboxData = + static_cast(sandboxPoolData->SandboxData()); + MOZ_ASSERT(sandboxData); + + auto* sandbox = sandboxData->Sandbox(); + + // Transfer aData into the sandbox. + + auto data = TransferBufferToWOFF2(sandbox, aData, aLength); + NS_ENSURE_TRUE(*data, false); + + // Validator for the decompression size. + // Returns the size and sets validateOK to true if size is valid (and false + // otherwise). + bool validateOK = false; + auto sizeValidator = [aLength, &validateOK](auto size) { + validateOK = false; + if (size < aLength) { + NS_WARNING("Size of decompressed WOFF 2.0 is less than compressed size"); + } else if (size == 0) { + NS_WARNING("Size of decompressed WOFF 2.0 is set to 0"); + } else if (size > OTS_MAX_DECOMPRESSED_FILE_SIZE) { + NS_WARNING( + nsPrintfCString("Size of decompressed WOFF 2.0 font exceeds %gMB", + OTS_MAX_DECOMPRESSED_FILE_SIZE / (1024.0 * 1024.0)) + .get()); + } else { + validateOK = true; + } + return size; + }; + + // Get the (estimated) decompression size and validate it. + + size_t decompressedSize = + sandbox + ->invoke_sandbox_function(RLBoxComputeWOFF2FinalSize, *data, aLength) + .copy_and_verify(sizeValidator); + + if (NS_WARN_IF(!validateOK)) { + return false; + } + + // Perform the actual conversion to TTF. + + auto sizep = WOFF2Alloc(sandbox); + auto bufp = WOFF2Alloc(sandbox); + auto bufOwnerString = + WOFF2Alloc(sandbox); // pointer to string that owns the bufer + + if (!sandbox + ->invoke_sandbox_function(RLBoxConvertWOFF2ToTTF, *data, aLength, + decompressedSize, sizep.get(), + bufOwnerString.get(), bufp.get()) + .unverified_safe_because( + "The ProcessTT* functions validate the decompressed data.")) { + return false; + } + + auto bufCleanup = mozilla::MakeScopeExit([&sandbox, &bufOwnerString] { + // Delete the string created by RLBoxConvertWOFF2ToTTF. + sandbox->invoke_sandbox_function(RLBoxDeleteWOFF2String, + bufOwnerString.get()); + }); + + // Get the actual decompression size and validate it. + // We need to validate the size again. RLBoxConvertWOFF2ToTTF works even if + // the computed size (with RLBoxComputeWOFF2FinalSize) is wrong, so we can't + // trust the decompressedSize to be the same as size sizep. + size_t size = (*sizep.get()).copy_and_verify(sizeValidator); + + if (NS_WARN_IF(!validateOK)) { + return false; + } + + const uint8_t* decompressed = + (*bufp.get()) + .unverified_safe_pointer_because( + size, "Only care that the buffer is within sandbox boundary."); + + // Since ProcessTT* memcpy from the buffer, make sure it's not null. + NS_ENSURE_TRUE(decompressed, false); + + if (aData[4] == 't' && aData[5] == 't' && aData[6] == 'c' && + aData[7] == 'f') { + return aProcessTTC(aHeader, aOutput, decompressed, size, aIndex); + } + ots::Font font(aHeader); + return aProcessTTF(aHeader, &font, aOutput, decompressed, size, 0); +} diff --git a/gfx/ots/RLBoxWOFF2Host.h b/gfx/ots/RLBoxWOFF2Host.h new file mode 100644 index 000000000000..94a6f3c660b8 --- /dev/null +++ b/gfx/ots/RLBoxWOFF2Host.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MODULES_WOFF2_RLBOXWOFF2_HOST_H_ +#define MODULES_WOFF2_RLBOXWOFF2_HOST_H_ + +#include "RLBoxWOFF2Types.h" + +// Load general firefox configuration of RLBox +#include "mozilla/rlbox/rlbox_config.h" + +#ifdef MOZ_WASM_SANDBOXING_WOFF2 +// Include the generated header file so that we are able to resolve the symbols +// in the wasm binary +# include "rlbox.wasm.h" +# define RLBOX_USE_STATIC_CALLS() rlbox_wasm2c_sandbox_lookup_symbol +# include "mozilla/rlbox/rlbox_wasm2c_sandbox.hpp" +#else +// Extra configuration for no-op sandbox +# define RLBOX_USE_STATIC_CALLS() rlbox_noop_sandbox_lookup_symbol +# include "mozilla/rlbox/rlbox_noop_sandbox.hpp" +#endif + +#include "mozilla/rlbox/rlbox.hpp" + +#include "woff2/RLBoxWOFF2Sandbox.h" +#include "./src/ots.h" + +class RLBoxWOFF2SandboxData : public mozilla::RLBoxSandboxDataBase { + friend class RLBoxWOFF2SandboxPool; + + public: + RLBoxWOFF2SandboxData(mozilla::UniquePtr aSandbox); + ~RLBoxWOFF2SandboxData(); + + rlbox_sandbox_woff2* Sandbox() const { return mSandbox.get(); } + + private: + mozilla::UniquePtr mSandbox; + sandbox_callback_woff2 mDecompressCallback; +}; + +using ProcessTTCFunc = bool(ots::FontFile* aHeader, ots::OTSStream* aOutput, + const uint8_t* aData, size_t aLength, + uint32_t aIndex); + +using ProcessTTFFunc = bool(ots::FontFile* aHeader, ots::Font* aFont, + ots::OTSStream* aOutput, const uint8_t* aData, + size_t aLength, uint32_t aOffset); + +bool RLBoxProcessWOFF2(ots::FontFile* aHeader, ots::OTSStream* aOutput, + const uint8_t* aData, size_t aLength, uint32_t aIndex, + ProcessTTCFunc* aProcessTTC, + ProcessTTFFunc* aProcessTTF); +#endif diff --git a/gfx/ots/RLBoxWOFF2Types.h b/gfx/ots/RLBoxWOFF2Types.h new file mode 100644 index 000000000000..6b5f81673e20 --- /dev/null +++ b/gfx/ots/RLBoxWOFF2Types.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MODULES_WOFF2_RLBOXWOFF2TYPES_H_ +#define MODULES_WOFF2_RLBOXWOFF2TYPES_H_ + +#include +#include "mozilla/rlbox/rlbox_types.hpp" +#include "woff2/decode.h" + +#ifdef MOZ_WASM_SANDBOXING_WOFF2 +RLBOX_DEFINE_BASE_TYPES_FOR(woff2, wasm2c) +#else +RLBOX_DEFINE_BASE_TYPES_FOR(woff2, noop) +#endif + +#include "mozilla/RLBoxSandboxPool.h" +#include "mozilla/StaticPtr.h" + +class RLBoxWOFF2SandboxPool : public mozilla::RLBoxSandboxPool { + public: + explicit RLBoxWOFF2SandboxPool(size_t aDelaySeconds) + : RLBoxSandboxPool(aDelaySeconds) {} + + static mozilla::StaticRefPtr sSingleton; + static void Initalize(size_t aDelaySeconds = 10); + + protected: + mozilla::UniquePtr CreateSandboxData() + override; + ~RLBoxWOFF2SandboxPool() = default; +}; + +#endif diff --git a/gfx/ots/ots-rlbox.patch b/gfx/ots/ots-rlbox.patch new file mode 100644 index 000000000000..8989cdb66746 --- /dev/null +++ b/gfx/ots/ots-rlbox.patch @@ -0,0 +1,56 @@ +diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc +--- a/gfx/ots/src/ots.cc ++++ b/gfx/ots/src/ots.cc +@@ -14,7 +14,7 @@ + #include + #include + +-#include ++#include "../RLBoxWOFF2Host.h" + + // The OpenType Font File + // http://www.microsoft.com/typography/otspec/otff.htm +@@ -511,39 +511,9 @@ bool ProcessWOFF(ots::FontFile *header, + return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file); + } + +-bool ProcessWOFF2(ots::FontFile *header, +- ots::OTSStream *output, +- const uint8_t *data, +- size_t length, +- uint32_t index) { +- size_t decompressed_size = woff2::ComputeWOFF2FinalSize(data, length); +- +- if (decompressed_size < length) { +- return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is less than compressed size"); +- } +- +- if (decompressed_size == 0) { +- return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0"); +- } +- // decompressed font must be <= OTS_MAX_DECOMPRESSED_FILE_SIZE +- if (decompressed_size > OTS_MAX_DECOMPRESSED_FILE_SIZE) { +- return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds %gMB", +- OTS_MAX_DECOMPRESSED_FILE_SIZE / (1024.0 * 1024.0)); +- } +- +- std::string buf(decompressed_size, 0); +- woff2::WOFF2StringOut out(&buf); +- if (!woff2::ConvertWOFF2ToTTF(data, length, &out)) { +- return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT"); +- } +- const uint8_t *decompressed = reinterpret_cast(buf.data()); +- +- if (data[4] == 't' && data[5] == 't' && data[6] == 'c' && data[7] == 'f') { +- return ProcessTTC(header, output, decompressed, out.Size(), index); +- } else { +- ots::Font font(header); +- return ProcessTTF(header, &font, output, decompressed, out.Size()); +- } ++bool ProcessWOFF2(ots::FontFile* header, ots::OTSStream* output, ++ const uint8_t* data, size_t length, uint32_t index) { ++ return RLBoxProcessWOFF2(header, output, data, length, index, ProcessTTC, ProcessTTF); + } + + ots::TableAction GetTableAction(const ots::FontFile *header, uint32_t tag) { + diff --git a/gfx/ots/src/moz.build b/gfx/ots/src/moz.build index 7ea8d1cdc89b..874032828463 100644 --- a/gfx/ots/src/moz.build +++ b/gfx/ots/src/moz.build @@ -7,6 +7,11 @@ EXPORTS += [ '../include/opentype-sanitiser.h', '../include/ots-memory-stream.h', + '../RLBoxWOFF2Types.h', +] + +UNIFIED_SOURCES += [ + '../RLBoxWOFF2Host.cpp' ] UNIFIED_SOURCES += [ @@ -71,5 +76,6 @@ USE_LIBS += [ ] LOCAL_INCLUDES += [ + '!/security/rlbox', '/modules/woff2/src', ] diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc index b41566fa22bf..bbebeb6ac5d0 100644 --- a/gfx/ots/src/ots.cc +++ b/gfx/ots/src/ots.cc @@ -14,7 +14,7 @@ #include #include -#include +#include "../RLBoxWOFF2Host.h" // The OpenType Font File // http://www.microsoft.com/typography/otspec/otff.htm @@ -511,39 +511,9 @@ bool ProcessWOFF(ots::FontFile *header, return ProcessGeneric(header, font, woff_tag, output, data, length, tables, file); } -bool ProcessWOFF2(ots::FontFile *header, - ots::OTSStream *output, - const uint8_t *data, - size_t length, - uint32_t index) { - size_t decompressed_size = woff2::ComputeWOFF2FinalSize(data, length); - - if (decompressed_size < length) { - return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is less than compressed size"); - } - - if (decompressed_size == 0) { - return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0"); - } - // decompressed font must be <= OTS_MAX_DECOMPRESSED_FILE_SIZE - if (decompressed_size > OTS_MAX_DECOMPRESSED_FILE_SIZE) { - return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds %gMB", - OTS_MAX_DECOMPRESSED_FILE_SIZE / (1024.0 * 1024.0)); - } - - std::string buf(decompressed_size, 0); - woff2::WOFF2StringOut out(&buf); - if (!woff2::ConvertWOFF2ToTTF(data, length, &out)) { - return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT"); - } - const uint8_t *decompressed = reinterpret_cast(buf.data()); - - if (data[4] == 't' && data[5] == 't' && data[6] == 'c' && data[7] == 'f') { - return ProcessTTC(header, output, decompressed, out.Size(), index); - } else { - ots::Font font(header); - return ProcessTTF(header, &font, output, decompressed, out.Size()); - } +bool ProcessWOFF2(ots::FontFile* header, ots::OTSStream* output, + const uint8_t* data, size_t length, uint32_t index) { + return RLBoxProcessWOFF2(header, output, data, length, index, ProcessTTC, ProcessTTF); } ots::TableAction GetTableAction(const ots::FontFile *header, uint32_t tag) { diff --git a/gfx/ots/sync.sh b/gfx/ots/sync.sh index 81738f9e1d78..162407af9acc 100755 --- a/gfx/ots/sync.sh +++ b/gfx/ots/sync.sh @@ -36,3 +36,6 @@ patch -p3 < ots-visibility.patch echo "Applying ots-lz4.patch..." patch -p3 < ots-lz4.patch + +echo "Applying ots-rlbox.patch..." +patch -p3 < ots-rlbox.patch diff --git a/layout/build/moz.build b/layout/build/moz.build index d8893cc04501..4ddc8703e2e6 100644 --- a/layout/build/moz.build +++ b/layout/build/moz.build @@ -19,6 +19,7 @@ UNIFIED_SOURCES += [ include("/ipc/chromium/chromium-config.mozbuild") LOCAL_INCLUDES += [ + "!/security/rlbox", "../base", "../forms", "../generic", diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 36cd3e1eaf23..7d20e43b3a6e 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -129,6 +129,7 @@ #include "mozilla/intl/nsComplexBreaker.h" #include "nsRLBoxExpatDriver.h" +#include "RLBoxWOFF2Types.h" using namespace mozilla; using namespace mozilla::net; @@ -298,6 +299,8 @@ nsresult nsLayoutStatics::Initialize() { RLBoxExpatSandboxPool::Initialize(); + RLBoxWOFF2SandboxPool::Initalize(); + return NS_OK; } diff --git a/modules/woff2/README.mozilla b/modules/woff2/README.mozilla index 7ff15836d349..bd35dc45209e 100644 --- a/modules/woff2/README.mozilla +++ b/modules/woff2/README.mozilla @@ -12,3 +12,5 @@ The in-tree copy is updated by running from within the modules/woff2 directory. Current version: [commit 1bccf208bca986e53a647dfe4811322adb06ecf8]. + +Additional patch: woff2-rlbox.patch (bug 1732201). diff --git a/modules/woff2/RLBoxWOFF2Sandbox.cpp b/modules/woff2/RLBoxWOFF2Sandbox.cpp new file mode 100644 index 000000000000..77d3ed16ab07 --- /dev/null +++ b/modules/woff2/RLBoxWOFF2Sandbox.cpp @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 +#include +#include "RLBoxWOFF2Sandbox.h" + +bool RLBoxConvertWOFF2ToTTF(const uint8_t* aData, size_t aLength, + size_t aDecompressedSize, size_t* aResultSize, + void** aResultOwningStr, uint8_t** aResultData) { + std::unique_ptr buf = + std::make_unique(aDecompressedSize, 0); + woff2::WOFF2StringOut out(buf.get()); + if (!woff2::ConvertWOFF2ToTTF(aData, aLength, &out)) { + return false; + } + *aResultSize = out.Size(); + // Return the string and its underlying C string. We need both to make sure we + // can free the string (which we do with RLBoxDeleteWOFF2String). + *aResultData = reinterpret_cast(buf->data()); + *aResultOwningStr = static_cast(buf.release()); + return true; +} + +void RLBoxDeleteWOFF2String(void** aStr) { + std::string* buf = static_cast(*aStr); + delete buf; +} + +size_t RLBoxComputeWOFF2FinalSize(const uint8_t* aData, size_t aLength) { + return woff2::ComputeWOFF2FinalSize(aData, aLength); +} + +BrotliDecompressCallback* sRLBoxBrotliDecompressCallback = nullptr; + +void RegisterWOFF2Callback(BrotliDecompressCallback* aCallback) { +#ifdef MOZ_IN_WASM_SANDBOX + // When Woff2 is wasmboxed, we need to register a callback for brotli + // decompression. The easiest way to store this is in a static variable. This + // is thread-safe because each (potentially-concurrent) woff2 instance gets + // its own sandbox with its own copy of the statics. + // + // When the sandbox is disabled (replaced with the noop sandbox), setting the + // callback is actually racey. However, we don't actually need a callback in + // that case, and can just invoke brotli directly. + sRLBoxBrotliDecompressCallback = aCallback; +#endif +} + +BrotliDecoderResult RLBoxBrotliDecoderDecompress(size_t aEncodedSize, + const uint8_t* aEncodedBuffer, + size_t* aDecodedSize, + uint8_t* aDecodedBuffer) { +#ifdef MOZ_IN_WASM_SANDBOX + assert(sRLBoxBrotliDecompressCallback); + return sRLBoxBrotliDecompressCallback( + aEncodedSize, reinterpret_cast(aEncodedBuffer), aDecodedSize, + reinterpret_cast(aDecodedBuffer)); +#else + return BrotliDecoderDecompress(aEncodedSize, aEncodedBuffer, aDecodedSize, + aDecodedBuffer); +#endif +} diff --git a/modules/woff2/RLBoxWOFF2Sandbox.h b/modules/woff2/RLBoxWOFF2Sandbox.h new file mode 100644 index 000000000000..cb6ebbc1ccde --- /dev/null +++ b/modules/woff2/RLBoxWOFF2Sandbox.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MODULES_WOFF2_RLBOX_WOFF2_SANDBOX_H_ +#define MODULES_WOFF2_RLBOX_WOFF2_SANDBOX_H_ + +#include + +extern "C" { + +// Since RLBox doesn't support C++ APIs, we expose C wrappers for the WOFF2. +size_t RLBoxComputeWOFF2FinalSize(const uint8_t* aData, size_t aLength); +bool RLBoxConvertWOFF2ToTTF(const uint8_t* aData, size_t aLength, + size_t aDecompressedSize, size_t* aResultSize, + void** aResultOwningStr, uint8_t** aResultData); +// RLBoxDeleteWOFF2String is used to delete the C++ string allocated by +// RLBoxConvertWOFF2ToTTF. +void RLBoxDeleteWOFF2String(void** aStr); + +// Type of brotli decoder function. Because RLBox doesn't (yet) cleanly support +// {size,uint8}_t types for callbacks, we're using unsigned long instead of +// size_t and char instead of uint8_t. + +typedef BrotliDecoderResult(BrotliDecompressCallback)( + unsigned long aEncodedSize, const char* aEncodedBuffer, + unsigned long* aDecodedSize, char* aDecodedBuffer); + +// Callback to the unsandboxed Brotli. + +extern BrotliDecompressCallback* sRLBoxBrotliDecompressCallback; + +void RegisterWOFF2Callback(BrotliDecompressCallback* aCallback); +BrotliDecoderResult RLBoxBrotliDecoderDecompress(size_t aEncodedSize, + const uint8_t* aEncodedBuffer, + size_t* aDecodedSize, + uint8_t* aDecodedBuffer); +}; + +#endif diff --git a/modules/woff2/moz.build b/modules/woff2/moz.build index 2076ec7fd048..fc60f993408f 100644 --- a/modules/woff2/moz.build +++ b/modules/woff2/moz.build @@ -4,24 +4,24 @@ # 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("sources.mozbuild") + with Files('**'): BUG_COMPONENT = ('Core', 'Graphics: Text') -UNIFIED_SOURCES += [ - 'src/table_tags.cc', - 'src/variable_length.cc', - 'src/woff2_common.cc', - 'src/woff2_dec.cc', - 'src/woff2_out.cc', -] +UNIFIED_SOURCES += woff2_sources EXPORTS.woff2 += [ 'include/woff2/decode.h', 'include/woff2/encode.h', 'include/woff2/output.h', + 'RLBoxWOFF2Sandbox.h', + ] # We allow warnings for third-party code that can be updated from upstream. AllowCompilerWarnings() Library('woff2') + +REQUIRES_UNIFIED_BUILD = True diff --git a/modules/woff2/sources.mozbuild b/modules/woff2/sources.mozbuild new file mode 100644 index 000000000000..74ec9c9952dd --- /dev/null +++ b/modules/woff2/sources.mozbuild @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +woff2_sources = [ + 'RLBoxWOFF2Sandbox.cpp', + 'src/table_tags.cc', + 'src/variable_length.cc', + 'src/woff2_common.cc', + 'src/woff2_dec.cc', + 'src/woff2_out.cc', +] diff --git a/modules/woff2/src/woff2_dec.cc b/modules/woff2/src/woff2_dec.cc index 8186c8e5d9e5..50806d7d19cc 100644 --- a/modules/woff2/src/woff2_dec.cc +++ b/modules/woff2/src/woff2_dec.cc @@ -19,7 +19,6 @@ #include #include -#include #include "./buffer.h" #include "./port.h" #include "./round.h" @@ -28,6 +27,8 @@ #include "./variable_length.h" #include "./woff2_common.h" +#include "../RLBoxWOFF2Sandbox.h" + namespace woff2 { namespace { @@ -758,7 +759,7 @@ bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, const uint8_t* src_buf, size_t src_size) { size_t uncompressed_size = dst_size; - BrotliDecoderResult result = BrotliDecoderDecompress( + BrotliDecoderResult result = RLBoxBrotliDecoderDecompress( src_size, src_buf, &uncompressed_size, dst_buf); if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || uncompressed_size != dst_size)) { diff --git a/modules/woff2/update.sh b/modules/woff2/update.sh index 9a9b7c883576..1f3c34cd8e3b 100755 --- a/modules/woff2/update.sh +++ b/modules/woff2/update.sh @@ -22,3 +22,6 @@ echo "###" echo "### Updated woff2 to $COMMIT." echo "### Remember to verify and commit the changes to source control!" echo "###" + +echo "Applying woff2-rlbox.patch..." +patch -p3 < woff2-rlbox.patch diff --git a/modules/woff2/woff2-rlbox.patch b/modules/woff2/woff2-rlbox.patch new file mode 100644 index 000000000000..28fd86734527 --- /dev/null +++ b/modules/woff2/woff2-rlbox.patch @@ -0,0 +1,29 @@ +diff --git a/modules/woff2/src/woff2_dec.cc b/modules/woff2/src/woff2_dec.cc +--- a/modules/woff2/src/woff2_dec.cc ++++ b/modules/woff2/src/woff2_dec.cc +@@ -19,7 +19,6 @@ + #include + #include + +-#include + #include "./buffer.h" + #include "./port.h" + #include "./round.h" +@@ -28,6 +27,8 @@ + #include "./variable_length.h" + #include "./woff2_common.h" + ++#include "../RLBoxWOFF2Sandbox.h" ++ + namespace woff2 { + + namespace { +@@ -758,7 +759,7 @@ bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, + bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, + const uint8_t* src_buf, size_t src_size) { + size_t uncompressed_size = dst_size; +- BrotliDecoderResult result = BrotliDecoderDecompress( ++ BrotliDecoderResult result = RLBoxBrotliDecoderDecompress( + src_size, src_buf, &uncompressed_size, dst_buf); + if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || + uncompressed_size != dst_size)) { diff --git a/parser/htmlparser/nsExpatDriver.cpp b/parser/htmlparser/nsExpatDriver.cpp index 637d6db582c1..c3dfca96efae 100644 --- a/parser/htmlparser/nsExpatDriver.cpp +++ b/parser/htmlparser/nsExpatDriver.cpp @@ -36,6 +36,7 @@ #include "nsThreadUtils.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/RLBoxUtils.h" #include "mozilla/UniquePtr.h" #include "mozilla/Logging.h" @@ -106,34 +107,12 @@ static const XML_Char* unverified_xml_string(uintptr_t ptr) { } /* The TransferBuffer class is used to copy (or directly expose in the - * noop-sandbox case) buffers into the sandbox that are automatically freed - * when the TransferBuffer is out of scope. NOTE: The sandbox lifetime must - * outlive all of its TransferBuffers. + * noop-sandbox case) buffers into the expat sandbox (and automatically + * when out of scope). */ template -class MOZ_STACK_CLASS TransferBuffer { - public: - TransferBuffer() = delete; - TransferBuffer(rlbox_sandbox_expat* aSandbox, const T* aBuf, - const size_t aLen) - : mSandbox(aSandbox), mCopied(false), mBuf(nullptr) { - if (aBuf) { - mBuf = rlbox::copy_memory_or_grant_access( - *mSandbox, aBuf, aLen * sizeof(T), false, mCopied); - } - }; - ~TransferBuffer() { - if (mCopied) { - mSandbox->free_in_sandbox(mBuf); - } - }; - tainted_expat operator*() const { return mBuf; }; - - private: - rlbox_sandbox_expat* mSandbox; - bool mCopied; - tainted_expat mBuf; -}; +using TransferBuffer = + mozilla::RLBoxTransferBufferToSandbox; /*************************** END RLBOX HELPERS ******************************/ diff --git a/security/rlbox/moz.build b/security/rlbox/moz.build index 3426652b0c7b..b2e742fbade2 100644 --- a/security/rlbox/moz.build +++ b/security/rlbox/moz.build @@ -75,3 +75,8 @@ if CONFIG["MOZ_WASM_SANDBOXING_EXPAT"]: for k, v in expat_defines: WASM_DEFINES[k] = v LOCAL_INCLUDES += ["/parser/expat/lib/"] + +if CONFIG["MOZ_WASM_SANDBOXING_WOFF2"]: + include("/modules/woff2/sources.mozbuild") + WASM_SOURCES += ["/modules/woff2/" + s for s in woff2_sources] + LOCAL_INCLUDES += ["/modules/woff2/include"] diff --git a/xpcom/base/RLBoxUtils.h b/xpcom/base/RLBoxUtils.h new file mode 100644 index 000000000000..c619df136284 --- /dev/null +++ b/xpcom/base/RLBoxUtils.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef SECURITY_RLBOX_UTILS_H_ +#define SECURITY_RLBOX_UTILS_H_ + +#include "mozilla/rlbox/rlbox_types.hpp" + +namespace mozilla { + +/* The RLBoxTransferBufferToSandbox class is used to copy (or directly expose in + * the noop-sandbox case) buffers into the sandbox that are automatically freed + * when the RLBoxTransferBufferToSandbox is out of scope. NOTE: The sandbox + * lifetime must outlive all of its RLBoxTransferBufferToSandbox. + */ +template +class MOZ_STACK_CLASS RLBoxTransferBufferToSandbox { + public: + RLBoxTransferBufferToSandbox() = delete; + RLBoxTransferBufferToSandbox(rlbox::rlbox_sandbox* aSandbox, const T* aBuf, + const size_t aLen) + : mSandbox(aSandbox), mCopied(false), mBuf(nullptr) { + if (aBuf) { + mBuf = rlbox::copy_memory_or_grant_access( + *mSandbox, aBuf, aLen * sizeof(T), false, mCopied); + } + }; + ~RLBoxTransferBufferToSandbox() { + if (mCopied) { + mSandbox->free_in_sandbox(mBuf); + } + }; + rlbox::tainted operator*() const { return mBuf; }; + + private: + rlbox::rlbox_sandbox* mSandbox; + bool mCopied; + rlbox::tainted mBuf; +}; + +/* The RLBoxAllocateInSandbox class is used to allocate data int sandbox that is + * automatically freed when the RLBoxAllocateInSandbox is out of scope. NOTE: + * The sandbox lifetime must outlive all of its RLBoxAllocateInSandbox'ations. + */ +template +class MOZ_STACK_CLASS RLBoxAllocateInSandbox { + public: + RLBoxAllocateInSandbox() = delete; + explicit RLBoxAllocateInSandbox(rlbox::rlbox_sandbox* aSandbox) + : mSandbox(aSandbox) { + mPtr = mSandbox->template malloc_in_sandbox(); + }; + ~RLBoxAllocateInSandbox() { + if (mPtr) { + mSandbox->free_in_sandbox(mPtr); + } + }; + rlbox::tainted get() const { return mPtr; }; + + private: + rlbox::rlbox_sandbox* mSandbox; + rlbox::tainted mPtr; +}; + +} // namespace mozilla + +#endif diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index a4d56668dfb9..07aa783b4727 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -131,6 +131,7 @@ EXPORTS.mozilla += [ "NSPRLogModulesParser.h", "OwningNonNull.h", "RLBoxSandboxPool.h", + "RLBoxUtils.h", "ShutdownPhase.h", "SizeOfState.h", "StaticLocalPtr.h",