From bff354f59de8f4545110c9acaf053ad9954d7f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Sandmann=20Pedersen?= Date: Thu, 15 Oct 2026 14:03:09 +0000 Subject: [PATCH] [wasm_split] Fix crash when manifest starts with a blank line When the first line of the manifest file is blank, currFuncs is null, but will be dereferenced to check if the current module will be empty. This CL fixes that and also cleans up the logic to clarify how the state machine works. --- src/tools/wasm-split/wasm-split.cpp | 55 ++++++++++----------- test/lit/wasm-split/multi-split-errors.wast | 53 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 test/lit/wasm-split/multi-split-errors.wast diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index 112a001dd01..e2b665a3cb5 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -424,42 +424,41 @@ void multiSplitModule(const WasmSplitOptions& options) { bool newSection = true; std::unordered_set moduleNameSet; while (std::getline(manifest, line)) { - if (line.empty()) { - newSection = true; - if (currFuncs->empty() && !options.quiet) { - std::cerr << "warning: Module " << currModule << " will be empty\n"; - } - continue; - } - Name name = line; if (newSection) { - if (name.endsWith(":")) { - name = name.substr(0, name.size() - 1); - if (name.size() == 0) { - Fatal() << "Module name is empty\n"; - } + Name moduleName = line; + if (moduleName.endsWith(":")) { + moduleName = moduleName.substr(0, moduleName.size() - 1); + } + if (moduleName.size() == 0) { + Fatal() << "Module name is empty\n"; } - if (moduleNameSet.contains(name)) { - Fatal() << "Module name " << name << " is listed more than once\n"; + if (moduleNameSet.contains(moduleName)) { + Fatal() << "Module name " << moduleName + << " is listed more than once\n"; } - currModule = name; + currModule = moduleName; moduleNameSet.insert(currModule); config.secondaryNames.push_back(currModule); config.secondaryFuncs.emplace_back(std::set()); currFuncs = &config.secondaryFuncs.back(); newSection = false; - continue; - } - assert(currFuncs); - if (!canSplitFunc(name, wasm, options)) { - continue; - } - currFuncs->insert(name); - auto [it, inserted] = funcModules.insert({name, currModule}); - if (!inserted && it->second != currModule) { - Fatal() << "Function " << name << "cannot be assigned to module " - << currModule << "; it is already assigned to module " - << it->second << '\n'; + } else if (line.empty()) { + newSection = true; + if (currFuncs->empty() && !options.quiet) { + std::cerr << "warning: Module " << currModule << " will be empty\n"; + } + } else { + assert(currFuncs); + Name funcName = line; + if (canSplitFunc(funcName, wasm, options)) { + currFuncs->insert(funcName); + auto [it, inserted] = funcModules.try_emplace(funcName, currModule); + if (!inserted && it->second != currModule) { + Fatal() << "Function " << funcName << " cannot be assigned to module " + << currModule << "; it is already assigned to module " + << it->second << '\n'; + } + } } } diff --git a/test/lit/wasm-split/multi-split-errors.wast b/test/lit/wasm-split/multi-split-errors.wast new file mode 100644 index 00000000000..80ebc9e9d6a --- /dev/null +++ b/test/lit/wasm-split/multi-split-errors.wast @@ -0,0 +1,53 @@ +;; Check that a manifest starting with a blank line produces an error rather +;; than crashing. +;; RUN: echo "" > %t.blank.manifest +;; RUN: echo "1:" >> %t.blank.manifest +;; RUN: echo "foo" >> %t.blank.manifest +;; RUN: not wasm-split --multi-split %s --manifest %t.blank.manifest --out-prefix=%t -o %t.wasm 2>&1 \ +;; RUN: | filecheck %s --check-prefix=EMPTY-NAME + +;; Also check a module name consisting only of a colon. +;; RUN: echo ":" > %t.colon.manifest +;; RUN: echo "foo" >> %t.colon.manifest +;; RUN: not wasm-split --multi-split %s --manifest %t.colon.manifest --out-prefix=%t -o %t.wasm 2>&1 \ +;; RUN: | filecheck %s --check-prefix=EMPTY-NAME + +;; Check that an empty module followed by two blank lines warns about the empty +;; module and then errors on the second blank line. +;; RUN: echo "1:" > %t.empty-mod.manifest +;; RUN: echo "" >> %t.empty-mod.manifest +;; RUN: echo "" >> %t.empty-mod.manifest +;; RUN: not wasm-split --multi-split %s --manifest %t.empty-mod.manifest --out-prefix=%t -o %t.wasm 2>&1 \ +;; RUN: | filecheck %s --check-prefix=EMPTY-MOD + +;; Check that duplicate module names are rejected. +;; RUN: echo "1:" > %t.dup-mod.manifest +;; RUN: echo "foo" >> %t.dup-mod.manifest +;; RUN: echo "" >> %t.dup-mod.manifest +;; RUN: echo "1:" >> %t.dup-mod.manifest +;; RUN: echo "bar" >> %t.dup-mod.manifest +;; RUN: not wasm-split --multi-split %s --manifest %t.dup-mod.manifest --out-prefix=%t -o %t.wasm 2>&1 \ +;; RUN: | filecheck %s --check-prefix=DUP-MOD + +;; Check that assigning the same function to two modules is rejected. +;; RUN: echo "1:" > %t.dup-func.manifest +;; RUN: echo "foo" >> %t.dup-func.manifest +;; RUN: echo "" >> %t.dup-func.manifest +;; RUN: echo "2:" >> %t.dup-func.manifest +;; RUN: echo "foo" >> %t.dup-func.manifest +;; RUN: not wasm-split --multi-split %s --manifest %t.dup-func.manifest --out-prefix=%t -o %t.wasm 2>&1 \ +;; RUN: | filecheck %s --check-prefix=DUP-FUNC + +;; EMPTY-NAME: Fatal: Module name is empty + +;; EMPTY-MOD: warning: Module 1 will be empty +;; EMPTY-MOD: Fatal: Module name is empty + +;; DUP-MOD: Fatal: Module name 1 is listed more than once + +;; DUP-FUNC: Fatal: Function foo cannot be assigned to module 2; it is already assigned to module 1 + +(module + (func $foo) + (func $bar) +)