Bug 1848369 - Add checksum to Stencil XDR content. r=nbp

Differential Revision: https://phabricator.services.mozilla.com/D186025
This commit is contained in:
Tooru Fujisawa 2023-08-12 00:04:20 +00:00
parent c2089aeeb8
commit 0d04f4494b
5 changed files with 101 additions and 22 deletions

View File

@ -198,6 +198,7 @@ MSG_DEF(JSMSG_ALLOC_OVERFLOW, 0, JSEXN_INTERNALERR, "allocation size ov
MSG_DEF(JSMSG_BAD_BYTECODE, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 0, JSEXN_INTERNALERR, "buffer too small")
MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")
MSG_DEF(JSMSG_DECODE_FAILURE, 0, JSEXN_INTERNALERR, "failed to decode cache")
MSG_DEF(JSMSG_NEED_DIET, 1, JSEXN_INTERNALERR, "{0} too large")
MSG_DEF(JSMSG_OUT_OF_MEMORY, 0, JSEXN_INTERNALERR, "out of memory")
MSG_DEF(JSMSG_OVER_RECURSED, 0, JSEXN_INTERNALERR, "too much recursion")

View File

@ -1404,19 +1404,6 @@ static XDRResult VersionCheck(XDRState<mode>* xdr) {
return Ok();
}
template <XDRMode mode>
static XDRResult XDRStencilHeader(XDRState<mode>* xdr,
const JS::ReadOnlyDecodeOptions* maybeOptions,
RefPtr<ScriptSource>& source) {
// The XDR-Stencil header is inserted at beginning of buffer, but it is
// computed at the end the incremental-encoding process.
MOZ_TRY(VersionCheck(xdr));
MOZ_TRY(frontend::StencilXDR::codeSource(xdr, maybeOptions, source));
return Ok();
}
XDRResult XDRStencilEncoder::codeStencil(
const RefPtr<ScriptSource>& source,
const frontend::CompilationStencil& stencil) {
@ -1427,10 +1414,32 @@ XDRResult XDRStencilEncoder::codeStencil(
MOZ_TRY(frontend::StencilXDR::checkCompilationStencil(this, stencil));
MOZ_TRY(XDRStencilHeader(this, nullptr,
const_cast<RefPtr<ScriptSource>&>(source)));
MOZ_TRY(VersionCheck(this));
uint32_t dummy = 0;
size_t lengthOffset = buf->cursor();
MOZ_TRY(codeUint32(&dummy));
size_t hashOffset = buf->cursor();
MOZ_TRY(codeUint32(&dummy));
size_t contentOffset = buf->cursor();
MOZ_TRY(frontend::StencilXDR::codeSource(
this, nullptr, const_cast<RefPtr<ScriptSource>&>(source)));
MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(
this, const_cast<frontend::CompilationStencil&>(stencil)));
size_t endOffset = buf->cursor();
if (endOffset > UINT32_MAX) {
ReportOutOfMemory(fc());
return fail(JS::TranscodeResult::Throw);
}
uint32_t length = endOffset - contentOffset;
codeUint32At(&length, lengthOffset);
const uint8_t* contentBegin = buf->bufferAt(contentOffset);
uint32_t hash = mozilla::HashBytes(contentBegin, length);
codeUint32At(&hash, hashOffset);
return Ok();
}
@ -1478,7 +1487,23 @@ XDRResult XDRStencilDecoder::codeStencil(
auto resetOptions = mozilla::MakeScopeExit([&] { options_ = nullptr; });
options_ = &options;
MOZ_TRY(XDRStencilHeader(this, &options, stencil.source));
MOZ_TRY(VersionCheck(this));
uint32_t length;
MOZ_TRY(codeUint32(&length));
uint32_t hash;
MOZ_TRY(codeUint32(&hash));
const uint8_t* contentBegin;
MOZ_TRY(peekArray(length, &contentBegin));
uint32_t actualHash = mozilla::HashBytes(contentBegin, length);
if (MOZ_UNLIKELY(actualHash != hash)) {
return fail(JS::TranscodeResult::Failure_BadDecode);
}
MOZ_TRY(frontend::StencilXDR::codeSource(this, &options, stencil.source));
MOZ_TRY(frontend::StencilXDR::codeCompilationStencil(this, stencil));
return Ok();

View File

@ -156,12 +156,12 @@ class StencilXDR {
/*
* The structure of the Stencil XDR buffer is:
*
* 1. Header
* a. Version
* b. ScriptSource
* d. Alignment padding
* 2. Stencil
* a. CompilationStencil
* 1. Version
* 2. length of content
* 3. checksum of content
* 4. content
* a. ScriptSource
* b. CompilationStencil
*/
/*

View File

@ -632,12 +632,28 @@ DecodeStencilTask::DecodeStencilTask(JSContext* cx,
MOZ_ASSERT(JS::IsTranscodingBytecodeAligned(range.begin().get()));
}
static void ReportDecodeFailure(JS::FrontendContext* fc) {
js::ErrorMetadata metadata;
metadata.filename = JS::ConstUTF8CharsZ("<unknown>");
metadata.lineNumber = 0;
metadata.columnNumber = 0;
metadata.lineLength = 0;
metadata.tokenOffset = 0;
metadata.isMuted = false;
js::ReportCompileErrorLatin1(fc, std::move(metadata), nullptr,
JSMSG_DECODE_FAILURE);
}
void DecodeStencilTask::parse(FrontendContext* fc) {
JS::DecodeOptions decodeOptions(options);
JS::TranscodeResult tr =
JS::DecodeStencil(fc, decodeOptions, range, getter_AddRefs(stencil_));
if (tr != JS::TranscodeResult::Ok) {
if (tr != JS::TranscodeResult::Throw) {
ReportDecodeFailure(fc);
}
return;
}

View File

@ -92,6 +92,11 @@ class XDRBuffer<XDR_ENCODE> : public XDRBufferBase {
return nullptr;
}
uint8_t* bufferAt(size_t cursor) {
MOZ_ASSERT(cursor < buffer_.length());
return &buffer_[cursor];
}
private:
JS::TranscodeBuffer& buffer_;
};
@ -310,6 +315,38 @@ class XDRState : public XDRCoderBase {
XDRResult codeUint64(uint64_t* n) { return codeUintImpl(n); }
void codeUint32At(uint32_t* n, size_t cursor) {
if constexpr (mode == XDR_ENCODE) {
uint8_t* ptr = buf->bufferAt(cursor);
memcpy(ptr, n, sizeof(uint32_t));
} else {
MOZ_CRASH("not supported.");
}
}
const uint8_t* bufferAt(size_t cursor) const {
if constexpr (mode == XDR_ENCODE) {
return buf->bufferAt(cursor);
}
MOZ_CRASH("not supported.");
}
XDRResult peekArray(size_t n, const uint8_t** p) {
if constexpr (mode == XDR_DECODE) {
const uint8_t* ptr = buf->peek(n);
if (!ptr) {
return fail(JS::TranscodeResult::Failure_BadDecode);
}
*p = ptr;
return mozilla::Ok();
}
MOZ_CRASH("not supported.");
}
/*
* Use SFINAE to refuse any specialization which is not an enum. Uses of
* this function do not have to specialize the type of the enumerated field