You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(hermes-base): audit semantic operands and harden fallback (#84)
Preserve semantic operands with a second raw HBC audit, fail closed on unverifiable layouts, and bound compiler and verifier subprocesses. Fix speculative sourcemap rejection handling, zero-length Static Hermes functions, and debug-output cleanup after timeout or spawn failure. Add real HBC96/HBC98 and Metro bundle regression coverage, including compiler patch16/patch17 and Node 18 checks.
bun -e 'import {probeHbcVersion} from "./src/utils/hermes-base"; if (probeHbcVersion(process.env.HERMESC) !== Number(process.env.EXPECTED_HBC)) throw new Error("unexpected HBC version");'
Hermes projects: `bundle` always runs hermesc with `-output-source-map`, so the debug info section is stripped from the bytecode (15–40% smaller, same as React Native's own release builds). The Hermes sourcemap stays in the intermediate directory (`.pushy/intermedia/<platform>/<bundle>.map`, never packed into the ppk) and is composed with the packager map — `--sourcemap` is on by default since 2.23 (`--no-sourcemap` opts out). When `bundle` publishes, that final map is uploaded and archived with the version (`sourceMapKey`), so `pushy symbolicate` can map crash stacks — including Hermes `address at` frames — back to source later. `pushy publish <ppk> --sourcemap <file.map>` archives a map for a ppk built elsewhere; publishing without a map prints a warning.
89
89
90
-
Hermes delta mode (`-base-bytecode`): by default (`--hermesBase auto`) `bundle` compiles against the previous HBC of the same app, which keeps Hermes string IDs stable and makes hot-update patches 5–30× smaller. The base comes from the server (`GET /app/:id/hermesBase`), verified by sha256 and kept in a local cache (`.pushy/cache/<sha256>`, 500 MB / 20 files, `PUSHY_CACHE_DIR` / `--cacheMaxMb` to tune, `pushy cache [clean]` to inspect or clear). `--hermesBase none` disables it; `--hermesBase <file.hbc|.ppk|.apk|.ipa>` uses a local artifact (for example the store build). `--verifyHermesBase` (default on) additionally compiles without the base (concurrently with the base compile) and compares both disassemblies — literal buffers by content, then function by function, with only representation differences (string ids, operand widths, jump distances, buffer offsets) folded away; on any mismatch or failure the CLI falls back to the plain compile, so the feature can never block a release. The log names the first difference (function, line, both sides) or, separately, a dump that could not be read; set `PUSHY_HERMES_BASE_DEBUG=1` to keep both disassemblies (`hermes-base-dump-base.txt` / `hermes-base-dump-plain.txt` next to the intermediate directory) for a bug report. What the check covers, what it does not yet, and how to triage a rejection: [docs/hermes-base-verification.md](docs/hermes-base-verification.md). The result is reported at publish (`hermesBaseOutcome`: `used` / `rejected` / `dump-failed` / `none`, plus the first difference) so the server can watch the rejection rate across apps. `HERMESC=<path> bun run fuzz:hermes-base --rounds 300` compiles random programs against random bases and reports any build the check would wrongly reject (differential fuzzing of the normalization rules). Only hermesc builds that include the upstream delta-mode fix are used (classic `react-native/sdks/hermesc`, or `hermes-compiler` ≥ 250829098). If a base compile fails, the full hermesc output is written to `hermes-base-error.log` next to the intermediate directory. `--resetCache false` skips Metro's `--reset-cache` and reuses its transform cache, which makes repeated bundles much faster.
90
+
Hermes delta mode (`-base-bytecode`): by default (`--hermesBase auto`) `bundle` compiles against the previous HBC of the same app, which keeps Hermes string IDs stable and makes hot-update patches 5–30× smaller. The base comes from the server (`GET /app/:id/hermesBase`), verified by sha256 and kept in a local cache (`.pushy/cache/<sha256>`, 500 MB / 20 files, `PUSHY_CACHE_DIR` / `--cacheMaxMb` to tune, `pushy cache [clean]` to inspect or clear). `--hermesBase none` disables it; `--hermesBase <file.hbc|.ppk|.apk|.ipa>` uses a local artifact (for example the store build). `--verifyHermesBase` (default on) additionally compiles without the base (concurrently with the base compile) and checks both artifacts in two passes: readable disassembly followed by raw operands resolved against complete binary strings, constants, function references and control-flow targets. Quoted whitespace and branch destinations are preserved; unsupported or unreadable layouts fail closed. On a mismatch, verification failure or compiler timeout the CLI falls back to the plain compile; failures of the plain compiler or the final sourcemap still fail the build. The raw pass adds verification time and holds both HBC files in memory, but does not add another compile. The log names the first difference (function, line, both sides) or, separately, a dump that could not be read; set `PUSHY_HERMES_BASE_DEBUG=1` to keep both disassemblies (`hermes-base-dump-base.txt` / `hermes-base-dump-plain.txt` next to the intermediate directory) for a bug report. Probe, verification and compile/sourcemap process deadlines default to 30/120/300 seconds respectively, configurable in milliseconds via `PUSHY_HERMES_PROBE_TIMEOUT_MS`, `PUSHY_HERMES_VERIFY_TIMEOUT_MS` and `PUSHY_HERMES_COMPILE_TIMEOUT_MS`. What the check covers, what it does not yet, and how to triage a rejection: [docs/hermes-base-verification.md](docs/hermes-base-verification.md). The result is reported at publish (`hermesBaseOutcome`: `used` / `rejected` / `dump-failed` / `none`, plus the first difference) so the server can watch the rejection rate across apps. `HERMESC=<path> bun run fuzz:hermes-base --rounds 300` compiles random programs against random bases and reports any build the check would wrongly reject (differential fuzzing of the normalization rules). Only hermesc builds that include the upstream delta-mode fix are used (classic `react-native/sdks/hermesc`, or `hermes-compiler` ≥ 250829098). If a base compile fails, the full hermesc output is written to `hermes-base-error.log` next to the intermediate directory. `--resetCache false` skips Metro's `--reset-cache` and reuses its transform cache, which makes repeated bundles much faster.
0 commit comments