Skip to content

Skip breakpoints on read-only bytecode pages instead of aborting#2021

Open
tmikov wants to merge 1 commit into
facebook:static_hfrom
tmikov:n-api
Open

Skip breakpoints on read-only bytecode pages instead of aborting#2021
tmikov wants to merge 1 commit into
facebook:static_hfrom
tmikov:n-api

Conversation

@tmikov
Copy link
Copy Markdown
Contributor

@tmikov tmikov commented May 14, 2026

The debugger installs breakpoints by overwriting an opcode in the bytecode stream with Debugger, which requires the page to be writable. The patcher uses mprotect to make the page RW; on failure it called hermes_fatal and aborted the process.

If the bytecode lives in a read-only segment (e.g. statically linked into the binary as a const array, ending up in __DATA_CONST / __TEXT_CONST on macOS), mprotect is rejected by the OS (EACCES under hardened runtime) and the abort takes down the whole process. This is reachable in practice because step / restoration / on-load breakpoints install at offset 0 of every CodeBlock about to execute, so stepping into such a CodeBlock from anywhere kills the runtime.

Make the patch best-effort:

  • CodeBlock::installBreakpointAtOffset and the static makeWritable helper now return bool. On failure no state is modified.
  • Debugger::installBreakpoint returns BreakpointLocation* (nullptr on failure), rolling back the partial breakpointLocations_ entry it just inserted.
  • doSetNonUserBreakpoint and setOnLoadBreakpoint silently skip when installBreakpoint fails. Step / restoration / on-load breakpoints are best-effort hints; the only loss is that stepping cannot pause at the entry of a CodeBlock whose page cannot be patched.
  • setUserBreakpoint now returns bool so callers can propagate failure to their clients (e.g. CDP could surface a proper error back to DevTools instead of aborting).
  • The four call sites that re-install a previously-installed breakpoint (the page is known writable since the original install succeeded) assert the result.

@meta-cla meta-cla Bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label May 14, 2026
@meta-codesync
Copy link
Copy Markdown

meta-codesync Bot commented May 14, 2026

@tmikov has imported this pull request. If you are a Meta employee, you can view this in D105190367.

The debugger installs breakpoints by overwriting an opcode in the
bytecode stream with Debugger, which requires the page to be
writable. The patcher uses mprotect to make the page RW; on
failure it called hermes_fatal and aborted the process.

If the bytecode lives in a read-only segment (e.g. statically
linked into the binary as a const array, ending up in
__DATA_CONST / __TEXT_CONST on macOS), mprotect is rejected by
the OS (EACCES under hardened runtime) and the abort takes down
the whole process. This is reachable in practice because step /
restoration / on-load breakpoints install at offset 0 of every
CodeBlock about to execute, so stepping into such a CodeBlock
from anywhere kills the runtime.

Make the patch best-effort:

- CodeBlock::installBreakpointAtOffset and the static makeWritable
  helper now return bool. On failure no state is modified.
- Debugger::installBreakpoint returns BreakpointLocation* (nullptr
  on failure), rolling back the partial breakpointLocations_ entry
  it just inserted.
- doSetNonUserBreakpoint and setOnLoadBreakpoint silently skip when
  installBreakpoint fails. Step / restoration / on-load breakpoints
  are best-effort hints; the only loss is that stepping cannot
  pause at the entry of a CodeBlock whose page cannot be patched.
- setUserBreakpoint now returns bool so callers can propagate
  failure to their clients (e.g. CDP could surface a proper error
  back to DevTools instead of aborting).
- The four call sites that re-install a previously-installed
  breakpoint (the page is known writable since the original
  install succeeded) assert the result.
@facebook-github-tools
Copy link
Copy Markdown

@tmikov has updated the pull request. You must reimport the pull request before landing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant