Skip to content

BinaryenAddTagImport calls getGlobalOrNull instead of getTagOrNull #8272

@sumleo

Description

@sumleo

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

src/binaryen-c.cpp:5284

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:

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);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions