Skip to content

Commit

Permalink
Enforce that IP is saved for all JSFunction calls
Browse files Browse the repository at this point in the history
Summary:
Add an assert in the runtime that if there is a CodeBlock for the
current stack frame, then the IP that is saved must be inside it.

Reviewed By: tmikov

Differential Revision: D64502107

fbshipit-source-id: 3c6bb72b8fb4d82232875b128a5bf5aeeeafec25
  • Loading branch information
neildhar authored and facebook-github-bot committed Nov 20, 2024
1 parent 0b58f74 commit fd3d459
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 2 deletions.
10 changes: 10 additions & 0 deletions include/hermes/VM/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,10 @@ class Runtime : public RuntimeBase, public HandleRootOwner {
uint32_t noRJSLevel_{0};

friend class NoRJSScope;

/// If the function currently at the top of the stack is a JSFunction, assert
/// that the given IP falls within it.
void assertTopCodeBlockContainsIP(const inst::Inst *ip) const;
#endif

public:
Expand All @@ -1428,6 +1432,9 @@ class Runtime : public RuntimeBase, public HandleRootOwner {
/// return to the interpreter at a different IP. This allows things external
/// to the interpreter loop to affect the flow of bytecode execution.
inline void setCurrentIP(const inst::Inst *ip) {
#ifdef HERMES_SLOW_DEBUG
assertTopCodeBlockContainsIP(ip);
#endif
currentIP_ = ip;
}

Expand All @@ -1439,6 +1446,9 @@ class Runtime : public RuntimeBase, public HandleRootOwner {
assert(
(uintptr_t)currentIP_ != kInvalidCurrentIP &&
"Current IP unknown - this probably means a CAPTURE_IP_* is missing in the interpreter.");
#ifdef HERMES_SLOW_DEBUG
assertTopCodeBlockContainsIP(currentIP_);
#endif
return currentIP_;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/VM/JSError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,9 @@ ExecutionStatus JSError::recordStackTrace(
codeBlock = prev->getCalleeCodeBlock();
locals = prev->getSHLocals();
}
// Assert that if the caller is a JSFunction, the IP was saved. But to be
// defensive, we still check it below.
assert(!codeBlock || savedIP);
if (codeBlock && savedIP) {
stack->emplace_back(
BytecodeStackTraceInfo(codeBlock, codeBlock->getOffsetOf(savedIP)));
Expand Down
8 changes: 8 additions & 0 deletions lib/VM/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,14 @@ void Runtime::removeRuntimeModule(RuntimeModule *rm) {
}

#ifndef NDEBUG
void Runtime::assertTopCodeBlockContainsIP(const inst::Inst *ip) const {
// Check that if the topmost function is currently a JSFunction, then the IP
// is in that function.
if (currentFrame_ != StackFramePtr(registerStackStart_))
if (auto *codeBlock = currentFrame_.getCalleeCodeBlock())
assert(codeBlock->contains(ip) && "IP not in CodeBlock");
}

void Runtime::printArrayCensus(llvh::raw_ostream &os) {
// Do array capacity histogram.
// Map from array size to number of arrays that are that size.
Expand Down
4 changes: 2 additions & 2 deletions unittests/VMRuntime/InterpreterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ TEST_F(InterpreterTest, SimpleSmokeTest) {
ScopedNativeCallFrame frame(
runtime,
0,
HermesValue::encodeUndefinedValue(),
HermesValue::encodeNativePointer(codeBlock),
HermesValue::encodeUndefinedValue(),
HermesValue::encodeUndefinedValue());
ASSERT_FALSE(frame.overflowed());
Expand Down Expand Up @@ -380,7 +380,7 @@ TEST_F(InterpreterTest, RecursiveFactorialTest) {
ScopedNativeCallFrame newFrame(
runtime,
1,
HermesValue::encodeUndefinedValue(),
HermesValue::encodeNativePointer(codeBlock),
HermesValue::encodeUndefinedValue(),
HermesValue::encodeUndefinedValue());
ASSERT_FALSE(newFrame.overflowed());
Expand Down

0 comments on commit fd3d459

Please sign in to comment.