Skip to content

Commit

Permalink
Bug 1732201 - Sandbox woff2 in OTS using RLBox r=bholley
Browse files Browse the repository at this point in the history
  • Loading branch information
deian committed Nov 27, 2021
1 parent 766b146 commit 63f7ccd
Show file tree
Hide file tree
Showing 24 changed files with 628 additions and 72 deletions.
7 changes: 5 additions & 2 deletions .clang-format-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/.*
Expand Down Expand Up @@ -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/.*
Expand Down
2 changes: 1 addition & 1 deletion config/recurse.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions gfx/ots/README.mozilla
Original file line number Diff line number Diff line change
Expand Up @@ -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).
206 changes: 206 additions & 0 deletions gfx/ots/RLBoxWOFF2Host.cpp
Original file line number Diff line number Diff line change
@@ -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<BrotliDecoderResult> RLBoxBrotliDecoderDecompressCallback(
rlbox_sandbox_woff2& aSandbox, tainted_woff2<unsigned long> aEncodedSize,
tainted_woff2<const char*> aEncodedBuffer,
tainted_woff2<unsigned long*> aDecodedSize,
tainted_woff2<char*> 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<const uint8_t*>(
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<uint8_t*>(aDecodedBuffer.unverified_safe_pointer_because(
decodedSize, "Pointer fits within sandbox"));

BrotliDecoderResult res = BrotliDecoderDecompress(
encodedSize, encodedBuffer, &decodedSize, decodedBuffer);

*aDecodedSize = decodedSize;

return res;
}

UniquePtr<RLBoxSandboxDataBase> RLBoxWOFF2SandboxPool::CreateSandboxData() {
// Create woff2 sandbox
auto sandbox = MakeUnique<rlbox_sandbox_woff2>();

#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<RLBoxWOFF2SandboxData> sbxData =
MakeUnique<RLBoxWOFF2SandboxData>(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> RLBoxWOFF2SandboxPool::sSingleton;

void RLBoxWOFF2SandboxPool::Initalize(size_t aDelaySeconds) {
AssertIsOnMainThread();
RLBoxWOFF2SandboxPool::sSingleton = new RLBoxWOFF2SandboxPool(aDelaySeconds);
ClearOnShutdown(&RLBoxWOFF2SandboxPool::sSingleton);
}

RLBoxWOFF2SandboxData::RLBoxWOFF2SandboxData(
mozilla::UniquePtr<rlbox_sandbox_woff2> aSandbox)
: mSandbox(std::move(aSandbox)) {
MOZ_COUNT_CTOR(RLBoxWOFF2SandboxData);
}

RLBoxWOFF2SandboxData::~RLBoxWOFF2SandboxData() {
MOZ_ASSERT(mSandbox);
mDecompressCallback.unregister();
mSandbox->destroy_sandbox();
MOZ_COUNT_DTOR(RLBoxWOFF2SandboxData);
}

template <typename T>
using TransferBufferToWOFF2 =
mozilla::RLBoxTransferBufferToSandbox<T, rlbox_woff2_sandbox_type>;
template <typename T>
using WOFF2Alloc = mozilla::RLBoxAllocateInSandbox<T, rlbox_woff2_sandbox_type>;

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<const RLBoxWOFF2SandboxData*>(sandboxPoolData->SandboxData());
MOZ_ASSERT(sandboxData);

auto* sandbox = sandboxData->Sandbox();

// Transfer aData into the sandbox.

auto data = TransferBufferToWOFF2<uint8_t>(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<size_t>(sandbox);
auto bufp = WOFF2Alloc<uint8_t*>(sandbox);
auto bufOwnerString =
WOFF2Alloc<void*>(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);
}
58 changes: 58 additions & 0 deletions gfx/ots/RLBoxWOFF2Host.h
Original file line number Diff line number Diff line change
@@ -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<rlbox_sandbox_woff2> aSandbox);
~RLBoxWOFF2SandboxData();

rlbox_sandbox_woff2* Sandbox() const { return mSandbox.get(); }

private:
mozilla::UniquePtr<rlbox_sandbox_woff2> mSandbox;
sandbox_callback_woff2<BrotliDecompressCallback*> 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
37 changes: 37 additions & 0 deletions gfx/ots/RLBoxWOFF2Types.h
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>
#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<RLBoxWOFF2SandboxPool> sSingleton;
static void Initalize(size_t aDelaySeconds = 10);

protected:
mozilla::UniquePtr<mozilla::RLBoxSandboxDataBase> CreateSandboxData()
override;
~RLBoxWOFF2SandboxPool() = default;
};

#endif
56 changes: 56 additions & 0 deletions gfx/ots/ots-rlbox.patch
Original file line number Diff line number Diff line change
@@ -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 <map>
#include <vector>

-#include <woff2/decode.h>
+#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<const uint8_t*>(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) {

Loading

0 comments on commit 63f7ccd

Please sign in to comment.