callMain (Emscripten) copie les arguments sur la pile du WebAssembly (64 Ko) sans les retirer. Pendant une partie dans le navigateur, le même moteur reçoit des centaines de commandes : vers la 540e, la pile débordait et corrompait la mémoire du moteur (moteur muet dans Firefox, onglet planté dans Chromium, « memory access out of bounds » sous Node). Repéré par make test-browser. stackSave et stackRestore sont exportés et encadrent chaque commande ; un test en envoie 3000 à la suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
// Le moteur WebAssembly répond indéfiniment : une partie en navigateur
|
|
// envoie des centaines de commandes au même moteur chargé une fois.
|
|
//
|
|
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
|
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
|
//
|
|
// Régression : callMain (Emscripten) laisse ses arguments sur la pile du
|
|
// WebAssembly (64 Ko). Sans remise en place après chaque commande
|
|
// (web/engine-wasm.mjs), la pile débordait vers la 500e commande : erreur
|
|
// « memory access out of bounds » sous Node, moteur muet dans Firefox,
|
|
// onglet planté dans Chromium.
|
|
|
|
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
// Compilé dans build/web/tests/ : la racine du dépôt est trois dossiers plus
|
|
// haut.
|
|
const root = new URL("../../../", import.meta.url);
|
|
|
|
interface RawEngine {
|
|
runText(args: string[]): { text: string; exitCode: number };
|
|
}
|
|
|
|
test("le moteur répond à 3000 commandes à la suite", async () => {
|
|
const module = await import(new URL("build/wasm/engine.mjs", root).href);
|
|
const wrapper = await import(new URL("web/engine-wasm.mjs", root).href);
|
|
const engine: RawEngine = await wrapper.loadEngine(
|
|
module.default,
|
|
fileURLToPath(new URL("build/wasm/", root)),
|
|
);
|
|
for (let i = 0; i < 3000; i++) {
|
|
const { text, exitCode } = engine.runText(["position", "4HPwATDgc/ABMA", "AQEAAAAAAAAA"]);
|
|
assert.equal(exitCode, 0, `commande ${i}`);
|
|
assert.match(text, /"pip_count": 167/, `commande ${i}`);
|
|
}
|
|
});
|