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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions