Skip to content

BinaryenAddTagImport calls getGlobalOrNull instead of getTagOrNull #8272

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

Activity

  1. kripken commented on Feb 9, 2026

    @kripken
    Member

    That does look like a typo, thanks. From #4991

    Would you like to open a PR?

  2. added 2 commits that reference this issue on Feb 10, 2026
    c0a7569
    5dd79cc
  3. sumleo commented on Feb 10, 2026

    @sumleo
    ContributorAuthor

    Hey @kripken , could you have a look at #8281 if you have a moment?

    BTW, is it possible to have a look at #8282 for another bug?

    (P.S., I have several other bugs, should I create one issue for one bug? Or is there any best practice for batch bug reporting?)

  4. kripken commented on Feb 10, 2026

    @kripken
    Member

    One issue per bug is good. Unless you find several that are very closely related perhaps.

  5. added a commit that references this issue on Feb 10, 2026
    93267c5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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