Skip to content

Commit

Permalink
Bug 1848369 - Add checksum to Stencil XDR content. r=nbp, a=RyanVM
Browse files Browse the repository at this point in the history
  • Loading branch information
arai-a committed Aug 14, 2023
1 parent 1e26bc4 commit 9672c23
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 22 deletions.
1 change: 1 addition & 0 deletions js/public/friend/ErrorNumbers.msg
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,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")
Expand Down
57 changes: 41 additions & 16 deletions js/src/frontend/StencilXdr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1407,19 +1407,6 @@ static XDRResult VersionCheck(XDRState<mode>* xdr) {
return Ok();
}

template <XDRMode mode>
static XDRResult XDRStencilHeader(XDRState<mode>* xdr,
const JS::DecodeOptions* 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) {
Expand All @@ -1430,10 +1417,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();
}
Expand Down Expand Up @@ -1480,7 +1489,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();
Expand Down
12 changes: 6 additions & 6 deletions js/src/frontend/StencilXdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

/*
Expand Down
16 changes: 16 additions & 0 deletions js/src/vm/HelperThreads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,19 @@ 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) {
if (!compileStorage_.allocateInput(fc, options)) {
return;
Expand All @@ -751,6 +764,9 @@ void DecodeStencilTask::parse(FrontendContext* fc) {
bool succeeded = false;
(void)stencil_->deserializeStencils(fc, options, range, &succeeded);
if (!succeeded) {
if (!fc->hadErrors()) {
ReportDecodeFailure(fc);
}
stencil_ = nullptr;
return;
}
Expand Down
37 changes: 37 additions & 0 deletions js/src/vm/Xdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9672c23

Please sign in to comment.