Skip to content

Token holding more than one key triggers a use-after-free in pkcs11_get_key() #667

Description

@Mno-hime

Segmentation Fault / Crash Details

  • Signal / exit code: SIGSEGV
  • Reproducibility: always
  • Affected command or operation: dnssec-signzone -E pkcs11 -S -a -g -o rsasha256.example zone.rsasha256.example.db
  • First observed version: 0.4.19
  • Last known working version (if any): 0.4.18

Backtrace

  • (gdb) bt
#0  0x00007f837ffc7d62 in pkcs11_atomic_add (value=value@entry=0x66b0, amount=amount@entry=1, lock=lock@entry=0x66b4) at p11_misc.c:50
#1  0x00007f837ffc6eac in pkcs11_object_ref (obj=0x656c) at p11_key.c:1420
#2  0x00007f837ffc7005 in pkcs11_set_ex_data_evp_pkey (key=<optimized out>, pkey=0x56337e95c8b0) at p11_key.c:1729
#3  pkcs11_get_key (key0=0x56337e95b1e0, object_class=object_class@entry=2) at p11_key.c:1245
#4  0x00007f837ffcdd2f in PKCS11_get_public_key (pkey=<optimized out>) at p11_front.c:268
#5  0x00007f837ffc3cf1 in UTIL_CTX_get_pubkey_from_uri (ctx=<optimized out>, uri=<optimized out>, ui_method=<optimized out>, ui_data=<optimized out>) at util_uri.c:1526
#6  0x00007f837ffc06b2 in ENGINE_CTX_load_pubkey (ctx=0x56337e8faa70, uri=<optimized out>, ui_method=<optimized out>, ui_data=<optimized out>) at eng_back.c:172
#7  0x00007f83828fc5d6 in ENGINE_load_public_key () from /lib/x86_64-linux-gnu/libcrypto.so.3
#8  0x00007f8382f1bee8 in dst__openssl_fromlabel_engine (ppriv=0x7fffff4eff08, ppub=0x7fffff4eff00, pin=0x0, label=0x56337e9464a0 "pkcs11:token=softhsm2-keyfromlabel;object=keyfromlabel-zsk-rsasha256.example;pin-source=pin", engine=0x6 <error: Cannot access memory at address 0x6>, key_base_id=6) at openssl_link.c:245
#9  dst__openssl_fromlabel (key_base_id=key_base_id@entry=6, engine=engine@entry=0x56337e9466d0 "pkcs11", label=label@entry=0x56337e9464a0 "pkcs11:token=softhsm2-keyfromlabel;object=keyfromlabel-zsk-rsasha256.example;pin-source=pin", pin=pin@entry=0x0, ppub=ppub@entry=0x7fffff4eff00, ppriv=ppriv@entry=0x7fffff4eff08) at openssl_link.c:355
#10 0x00007f8382f1f917 in opensslrsa_fromlabel (key=key@entry=0x56337e947230, engine=engine@entry=0x56337e9466d0 "pkcs11", label=0x56337e9464a0 "pkcs11:token=softhsm2-keyfromlabel;object=keyfromlabel-zsk-rsasha256.example;pin-source=pin", pin=pin@entry=0x0) at opensslrsa_link.c:1190
#11 0x00007f8382f1feb7 in opensslrsa_parse (key=0x56337e947230, lexer=<optimized out>, pub=0x56337e945660) at opensslrsa_link.c:1089
#12 0x00007f8382ed3e13 in dst_key_fromnamedfile (filename=filename@entry=0x7fffff4f11e4 "Krsasha256.example.+008+42316.private", dirname=<optimized out>, dirname@entry=0x56337e1d8af6 ".", type=type@entry=234881024, mctx=mctx@entry=0x56337e9282b0, keyp=keyp@entry=0x7fffff4f01d0) at dst_api.c:660
#13 0x00007f8382eca665 in findmatchingkeys (directory=directory@entry=0x56337e1d8af6 ".", rrtypekey=rrtypekey@entry=false, namebuf=namebuf@entry=0x7fffff4f13a0 "rsasha256.example.", len=len@entry=18, mctx=mctx@entry=0x56337e9282b0, now=now@entry=1785153032, list=<optimized out>) at dnssec.c:1306
#14 0x00007f8382eca8f7 in dns_dnssec_findmatchingkeys (origin=<optimized out>, kasp=kasp@entry=0x0, keydir=<optimized out>, keystores=keystores@entry=0x0, now=<optimized out>, rrtypekey=rrtypekey@entry=false, mctx=<optimized out>, keylist=<optimized out>) at dnssec.c:1372
#15 0x000056337e1d3cbd in build_final_keylist () at dnssec-signzone.c:2861
#16 main (argc=<optimized out>, argv=<optimized out>) at dnssec-signzone.c:3961

Memory / Sanitizers

  • ASan / UBSan
  ERROR: AddressSanitizer: heap-use-after-free ... READ of size 8
      #0 pkcs11_set_ex_data_evp_pkey p11_key.c:1726
      #1 pkcs11_get_key p11_key.c:1245
  0x6040000328f0 is located 32 bytes inside of 40-byte region   <- offsetof(_private), sizeof(PKCS11_KEY)
  freed by thread T0 here:
      #0 __interceptor_realloc
      #1 pkcs11_init_key p11_key.c:1448

Crash Context

Anything that may be relevant:

  • OpenSSL provider / engine in use: 3.0.20 (Debian 12)
  • PKCS#11 modules
  • Custom OpenSSL configuration
  • Threading or concurrency

Environment

  • Operating system and version (e.g. Ubuntu 24.04): Debian 12
  • Architecture (x86_64, arm64, etc.): x86-64
  • PKCS#11 module used:
  • Token / HSM type: softhsm2

Versions

  • libp11 built from:
    • upstream master
    • upstream release (tag): 0.4.19
    • distribution package (name and version):
  • PKCS#11 module and version:
  • openssl version -a
OpenSSL 3.0.20 7 Apr 2026 (Library: OpenSSL 3.0.20 7 Apr 2026)
built on: Sat Jun  6 19:56:20 2026 UTC
platform: debian-amd64
options:  bn(64,64)
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -fzero-call-used-regs=used-gpr -DOPENSSL_TLS_SECURITY_LEVEL=2 -Wa,--noexecstack -g -O2 -ffile-prefix-map=/build/reproducible-path/openssl-3.0.20=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
OPENSSLDIR: "/usr/lib/ssl"
ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-3"
MODULESDIR: "/usr/lib/x86_64-linux-gnu/ossl-modules"
Seeding source: os-specific
CPUINFO: OPENSSL_ia32cap=0x7ef8320b078bffff:0x405fdef1bf97a9

Reproducer with BIND 9.20

#!/bin/bash
# Reproduce the libp11 0.4.19 SIGSEGV in dnssec-signzone -E pkcs11.
# Assumes: Debian 12, OpenSSL 3.x, SoftHSM2, libp11 0.4.19 installed in /usr,
# and a BIND 9.20 tree built with -DOPENSSL_API_COMPAT=10100.
set -eu

BIND=${BIND:-/home/agent/bind9}
WORK=${WORK:-$(mktemp -d)}
ENGINESDIR=$(openssl version -e | sed 's/.*"\(.*\)".*/\1/')

cd "$WORK"

# 1. A SoftHSM2 token with a KSK and a ZSK on it (>=2 keys is the trigger).
mkdir -p tokens
cat >softhsm2.conf <<EOF
directories.tokendir = $WORK/tokens
objectstore.backend = file
EOF
export SOFTHSM2_CONF=$WORK/softhsm2.conf
softhsm2-util --init-token --free --pin 1234 --so-pin 1234 \
	--label softhsm2-keyfromlabel >/dev/null

echo 1234 >pin
for id in keyfromlabel-ksk keyfromlabel-zsk; do
	label="$id-rsasha256.example"
	pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
		--token-label softhsm2-keyfromlabel -l -k --key-type rsa:2048 \
		--label "$label" \
		--id "$(printf %s "$label" | sha1sum | cut -d' ' -f1)" \
		--pin 1234 >/dev/null
done

# 2. The engine configuration the BIND CI image generates.
sed 's|^openssl_conf = .*|openssl_conf = openssl_init|' /etc/ssl/openssl.cnf >openssl.cnf
cat >>openssl.cnf <<EOF

[openssl_init]
engines=engine_section

[engine_section]
pkcs11 = pkcs11_section

[pkcs11_section]
engine_id = pkcs11
dynamic_path = $ENGINESDIR/pkcs11.so
MODULE_PATH = /usr/lib/softhsm/libsofthsm2.so
init = 0
EOF
export OPENSSL_CONF=$WORK/openssl.cnf

# 3. Pull both keys off the token and build a zone out of them.
for id in keyfromlabel-ksk keyfromlabel-zsk; do
	flag=""
	[ "$id" = keyfromlabel-ksk ] && flag="-f KSK"
	"$BIND"/bin/dnssec/dnssec-keyfromlabel -E pkcs11 -a rsasha256 -y \
		-l "pkcs11:token=softhsm2-keyfromlabel;object=$id-rsasha256.example;pin-source=pin" \
		$flag rsasha256.example >/dev/null
done
cat "$BIND"/bin/tests/system/keyfromlabel/template.db.in Krsasha256.example.*.key \
	>zone.rsasha256.example.db

# 4. Sign it -- this re-loads both keys through ENGINE_load_public_key().
ulimit -c unlimited
"$BIND"/bin/dnssec/dnssec-signzone -E pkcs11 -S -a -g \
	-o rsasha256.example zone.rsasha256.example.db
echo "signer exit=$?  (expected: killed by SIGSEGV, exit 139)"

Fix

This Opus 5 vibed fix works for me with 0.4.19:

--- pristine/libp11-libp11-0.4.19/src/libp11-int.h	2026-07-21 21:27:53.000000000 +0000
+++ libp11-libp11-0.4.19/src/libp11-int.h	2026-07-27 11:34:21.329322687 +0000
@@ -117,7 +117,6 @@
 	unsigned int forkid;
 	int refcnt;
 	pthread_mutex_t lock;
-	PKCS11_KEY *public; /* our current public object */
 };
 
 struct pkcs11_object_ops {
--- pristine/libp11-libp11-0.4.19/src/p11_key.c	2026-07-21 21:27:53.000000000 +0000
+++ libp11-libp11-0.4.19/src/p11_key.c	2026-07-27 11:34:16.579206792 +0000
@@ -157,7 +157,7 @@
 #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
-static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_KEY *key);
+static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key);
 static PKCS11_OBJECT_private *pkcs11_get_ex_data_evp_pkey(const EVP_PKEY *pkey);
 static void alloc_evp_pkey_ex_index(void);
 #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
@@ -1242,7 +1242,7 @@
 	/* Store the backing PKCS#11 object in EVP_PKEY ex_data. Public key
 	 * ex_data is needed as a workaround for FALCON token-side verify. */
 	alloc_evp_pkey_ex_index();
-	pkcs11_set_ex_data_evp_pkey(ret, key->public);
+	pkcs11_set_ex_data_evp_pkey(ret, key);
 #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
 err:
 	if (key != key0)
@@ -1461,9 +1461,6 @@
 	key->label = kpriv->label;
 	key->isPrivate = (type == CKO_PRIVATE_KEY);
 
-	/* Link back */
-	kpriv->public = key;
-
 	if (ret)
 		*ret = key;
 	return 0;
@@ -1719,14 +1716,14 @@
 }
 
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
-static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_KEY *key)
+static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key)
 {
 	PKCS11_OBJECT_private *obj;
 
-	if (pkey == NULL || key == NULL || key->_private == NULL)
+	if (pkey == NULL || key == NULL)
 		return;
 
-	obj = pkcs11_object_ref(key->_private);
+	obj = pkcs11_object_ref(key);
 	if (obj == NULL)
 		return;
 

Metadata

Metadata

Assignees

No one assigned

    Labels

    crashReport a segmentation fault

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions