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
Since the Grunt → webpack/Terser migration (#65, shipped in Euro-Office 9.3.4), the
built sdk-all.js bundles contain 131 unquoted supplementary-plane ("astral")
object keys, e.g. 𝕒:"\\doublea" in the LaTeX symbol table. Terser treats those
characters as valid identifiers and drops the quotes.
doctrenderer/x2t embed V8 8.9 built with v8_enable_i18n_support=false.
Without ICU, V8's reduced Unicode tables do not classify astral characters as ID_Start, so parsing the bundle fails with SyntaxError: Invalid or unexpected token. ScriptCompiler::Compile then returns an empty MaybeLocal, and an
unguarded .ToLocalChecked() turns that into a CHECK failure → __builtin_trap() → Illegal instruction (core dumped).
The previous Closure Compiler build emitted pure ASCII (everything escaped to \uXXXX), so this could never happen. The 9.3.2 bundle contains zero non-ASCII
bytes; the 9.3.4 bundle contains 43,348.
⚠️Euro-Office/DocumentServer#339 attributes the crash to Intel MPX. That is
incorrect — see "Ruled out" below. The Illegal instruction is V8 trapping
itself, not an unsupported CPU instruction.
Impact
Euro-Office 9.3.4 is unusable: .deb postinst, standalone image and
cluster-docs all fail during documentserver-generate-allfonts.sh, and every
document save/convert fails (callback status 7).
Affects amd64 and arm64 alike. Downgrading to 9.3.3 resolves it.
Affected bundles: word/sdk-all.js and slide/sdk-all.js (131 bare astral keys,
1,256 astral characters each).
Root cause chain
Terser config in build/webpack.sdk.factory.mjs sets neither format.ascii_only nor quote_keys. Defaults are raw UTF-8 output and
identifier-style (unquoted) object keys.
Rows 3/4 isolate the fault to the JS, not the binary. Row 5 rules out size.
Rows 9/10 are the 15-byte minimal case.
node --check passes on every variant above under Node 15 (V8 8.6), Node 16
(V8 9.4) and Node 22 (V8 12.4) — all ship full ICU. No Node- or Chromium-based
test can detect this.
ES-version skew / dropped Babel ES5 downlevel — the bundles parse on V8 8.6, older than the embedded 8.9.
Bundle bloat — 9.3.2's sdk-all.js is larger (19.7 MB vs 18.4 MB) and works.
Parser stack depth — both parse at --stack-size=600.
One-byte vs two-byte string representation — a single astral or Cyrillic
character inside a string literal compiles fine.
Invalid UTF-8 / lexical corruption — the file is valid UTF-8.
Leaked @@license-banner@@ sentinel — it sits inside /* */.
Stale code cache — fresh installs fail too; the 0-byte cache is a consequence.
Proposed fix (this repo)
In build/webpack.sdk.factory.mjs, terserOptions:
format: {comments: /@@license-banner@@/,ascii_only: true,// escape all non-ASCII as \uXXXX (restores Closure's invariant)},quote_keys: true,// force "𝕒": instead of 𝕒:
Either option alone is likely sufficient; together they are equivalent to the
configurations proven to compile (rows 5 and 9).
Regression test: assert the built bundles contain no byte > 0x7F. Cheap,
exact, and encodes the invariant the Closure build used to provide implicitly.
Follow-ups in other repos
Euro-Office/core — defensive hardening, worth its own issue:
guard the ToLocalChecked() at v8_base.cpp#L95 and surface the TryCatch
message; a JS syntax error must not abort the process;
do not create sdk-all.cache until CreateCodeCache() succeeds — an abort
currently leaves a 0-byte cache that poisons every later run;
evaluate v8_enable_i18n_support=true (separate trade-off: binary size, build
time). The fix above does not depend on it.
CI gap — this repo's check-build.yml validates bundles with Node 20 and
headless Chromium, both full-ICU, so it structurally cannot catch this class of
bug. The bundle needs to be parsed by the actual embedded V8. The three
doctrenderer suites tracked in Run existing test cases core#93 are exactly that gate; Enable osign gtest suite under CTest core#134 clears the last currently-unblocked suite.
Terser emits bare astral object keys in
sdk-all.js; doctrenderer's no-ICU V8 rejects them and x2t aborts (9.3.4 regression)Target repo:
Euro-Office/sdkjsLabels (suggested): bug, regression, build, priority:critical
Summary
Since the Grunt → webpack/Terser migration (#65, shipped in Euro-Office 9.3.4), the
built
sdk-all.jsbundles contain 131 unquoted supplementary-plane ("astral")object keys, e.g.
𝕒:"\\doublea"in the LaTeX symbol table. Terser treats thosecharacters as valid identifiers and drops the quotes.
doctrenderer/x2tembed V8 8.9 built withv8_enable_i18n_support=false.Without ICU, V8's reduced Unicode tables do not classify astral characters as
ID_Start, so parsing the bundle fails withSyntaxError: Invalid or unexpected token.ScriptCompiler::Compilethen returns an emptyMaybeLocal, and anunguarded
.ToLocalChecked()turns that into aCHECKfailure →__builtin_trap()→Illegal instruction (core dumped).The previous Closure Compiler build emitted pure ASCII (everything escaped to
\uXXXX), so this could never happen. The 9.3.2 bundle contains zero non-ASCIIbytes; the 9.3.4 bundle contains 43,348.
User-facing report: Euro-Office/DocumentServer#339 and
https://help.nextcloud.com/t/euro-office-upgrade-to-9-3-4-from-9-3-3-failed/248589
Impact
.debpostinst, standalone image andcluster-docs all fail during
documentserver-generate-allfonts.sh, and everydocument save/convert fails (callback status 7).
word/sdk-all.jsandslide/sdk-all.js(131 bare astral keys,1,256 astral characters each).
Root cause chain
build/webpack.sdk.factory.mjssets neitherformat.ascii_onlynorquote_keys. Defaults are raw UTF-8 output andidentifier-style (unquoted) object keys.
𝕒:"\\doublea",𝔸:"\\doubleA", … while BMPcharacters stay quoted (
"∙":"\\bullet").core/Common/3dParty/v8/tools/8.9/x64-linux-dynamic/nc-build.sh#L329(
arm64-linux-dynamicis identical).sdk-all-min.js+fonts_*.js+sdk-all.jsandcompiles the result as one script:
core/DesktopEditor/doctrenderer/editors.cpp#L69-L135core/DesktopEditor/doctrenderer/js_internal/v8/v8_base.cpp#L95— the only unguarded
ToLocalChecked()on that path; its siblings at L61, L83and L112 all test
IsEmpty()first.Minimal reproduction
Take the shipped 9.3.4
word/sdk-all.js, replace every non-ASCII byte withx(known-good, identical byte length), and append one line:
Oracle — inside
ghcr.io/euro-office/documentserver:v9.3.4, a non-zerosdk-all.cachemeans the script compiled:Evidence
word/sdk-all.cachesdk-all-min.js+ 9.3.2sdk-all.jssdk-all-min.js+ 9.3.4sdk-all.jsx, identical byte lengthvar __k={"𝕒":1};var __k={𝕒:1};Rows 3/4 isolate the fault to the JS, not the binary. Row 5 rules out size.
Rows 9/10 are the 15-byte minimal case.
node --checkpasses on every variant above under Node 15 (V8 8.6), Node 16(V8 9.4) and Node 22 (V8 12.4) — all ship full ICU. No Node- or Chromium-based
test can detect this.
Ruled out
arm64, where MPX does not exist.
older than the embedded 8.9.
sdk-all.jsis larger (19.7 MB vs 18.4 MB) and works.--stack-size=600.character inside a string literal compiles fine.
@@license-banner@@sentinel — it sits inside/* */.Proposed fix (this repo)
In
build/webpack.sdk.factory.mjs,terserOptions:Either option alone is likely sufficient; together they are equivalent to the
configurations proven to compile (rows 5 and 9).
Regression test: assert the built bundles contain no byte >
0x7F. Cheap,exact, and encodes the invariant the Closure build used to provide implicitly.
Follow-ups in other repos
Euro-Office/core— defensive hardening, worth its own issue:ToLocalChecked()atv8_base.cpp#L95and surface the TryCatchmessage; a JS syntax error must not abort the process;
sdk-all.cacheuntilCreateCodeCache()succeeds — an abortcurrently leaves a 0-byte cache that poisons every later run;
v8_enable_i18n_support=true(separate trade-off: binary size, buildtime). The fix above does not depend on it.
check-build.ymlvalidates bundles with Node 20 andheadless Chromium, both full-ICU, so it structurally cannot catch this class of
bug. The bundle needs to be parsed by the actual embedded V8. The three
doctrenderer suites tracked in Run existing test cases core#93 are exactly that gate;
Enable osign gtest suite under CTest core#134 clears the last currently-unblocked suite.
Related
ascii_only/quote_keys