#43092·bun

TLS SNI: names with more than 10 labels are added to the SNI tree but never found or removed

Author: robobunCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugnode.js

What happens

The native SNI tree in packages/bun-usockets/src/crypto/sni_tree.cpp has an asymmetric label limit. sni_add walks every label of a hostname. sni_remove and sni_find stop at 10 labels and return nullptr (MAX_LABELS is defined but unused). A name with more than 10 labels can be added, but it never matches a handshake and cannot be removed.

Visible effects:

  1. tls.Server#addContext(name, ctx) with a name of more than 10 labels is accepted, but a client that sends that servername gets the default certificate. Node selects ctx.
  2. Listener::add_server_name (src/runtime/socket/Listener.rs) removes, then adds. For such a name the remove does nothing and the add reports a duplicate. So two addContext() calls that land on one tree node (for example name and name + ".") make listen() throw Failed to register SNI for '...'. #43082 covers the half-open server that throw left behind, but not the throw itself.
  3. Bun.serve({ tls: [{ serverName }] }) uses the same tree and has the same limit.

Repro

Run from the repo root. The fixtures are in test/js/node/tls/fixtures/.

import tls from "node:tls";
import fs from "node:fs";
import { once } from "node:events";
const dir = "test/js/node/tls/fixtures/";
const pem = n => ({ key: fs.readFileSync(dir + n + "-key.pem", "utf8"), cert: fs.readFileSync(dir + n + "-cert.pem", "utf8") });
for (const name of ["a.b.c.d.e.f.g.h.i.example", "a.b.c.d.e.f.g.h.i.j.k.example"]) {
  const server = tls.createServer(pem("agent1"), s => s.end());
  server.addContext(name, pem("agent2"));
  server.listen(0, "127.0.0.1");
  await once(server, "listening");
  const c = tls.connect({ port: server.address().port, host: "127.0.0.1", servername: name, rejectUnauthorized: false });
  await once(c, "secureConnect");
  console.log(name.split(".").length, "labels ->", c.getPeerCertificate().subject.CN);
  c.end(); server.close();
}

bun 1.4.3, Linux x64:

10 labels -> agent2
12 labels -> agent1

node v26.3.0:

10 labels -> agent2
12 labels -> agent2

Expected

sni_remove and sni_find accept the same names as sni_add. A name of any label count that addContext() accepted is selected at the handshake and can be replaced.

Found during the work on #43082, where the duplicate-add throw is the only public input that reaches the failing listen() path.