-
Notifications
You must be signed in to change notification settings - Fork 838
Description
Summary
BinaryenAddTagImport in the C API looks up globals instead of tags when checking if the tag already exists. This is a copy-paste bug from BinaryenAddGlobalImport directly above it.
Location
void BinaryenAddTagImport(BinaryenModuleRef module,
const char* internalName,
const char* externalModuleName,
const char* externalBaseName,
BinaryenType params,
BinaryenType results) {
auto* tag = ((Module*)module)->getGlobalOrNull(internalName); // BUG: should be getTagOrNull
...
}For comparison, every other AddXxxImport function uses the correct lookup:
BinaryenAddGlobalImportcallsgetGlobalOrNullBinaryenAddMemoryImportcallsgetMemoryOrNullBinaryenAddTableImportcallsgetTableOrNull
Test case 1: Calling BinaryenAddTagImport twice with the same name
#include "binaryen-c.h"
int main() {
BinaryenModuleRef module = BinaryenModuleCreate();
// First call — creates the tag.
BinaryenAddTagImport(module, "myTag", "env", "originalTag",
BinaryenTypeInt32(), BinaryenTypeNone());
// Second call — should update module/base on the existing tag.
BinaryenAddTagImport(module, "myTag", "env2", "updatedTag",
BinaryenTypeInt32(), BinaryenTypeNone());
BinaryenModuleDispose(module);
return 0;
}Expected: The second call updates the existing tag's module and base fields (same behavior as calling BinaryenAddGlobalImport twice with the same global name).
Actual: The program aborts with:
Fatal: Module::addTag: myTag already exists
getGlobalOrNull("myTag") returns nullptr (no global with that name), so the function tries to add a duplicate tag.
Test case 2: Global with the same name as a tag import
#include "binaryen-c.h"
int main() {
BinaryenModuleRef module = BinaryenModuleCreate();
// Add a global named "sharedName".
BinaryenAddGlobal(module, "sharedName", BinaryenTypeInt32(), 0,
BinaryenConst(module, BinaryenLiteralInt32(0)));
// Add a tag import with the same name.
// Tags and globals have separate namespaces in wasm, so this is valid.
BinaryenAddTagImport(module, "sharedName", "env", "myTag",
BinaryenTypeInt32(), BinaryenTypeNone());
BinaryenModuleDispose(module);
return 0;
}Expected: A new tag is created. The global is not affected.
Actual: getGlobalOrNull("sharedName") finds the Global* and the code reinterprets it as a Tag*, writing module and base fields on the wrong object type. This is undefined behavior and crashes with SIGABRT.
Fix
Change getGlobalOrNull to getTagOrNull on line 5284:
- auto* tag = ((Module*)module)->getGlobalOrNull(internalName);
+ auto* tag = ((Module*)module)->getTagOrNull(internalName);