Bug#1135799: Poppler (GLib) with NSS crypto crashes on exit with using SoftHSM 2 PKCS#11 module
John Scott
jscott at posteo.net
Wed May 6 03:33:28 BST 2026
Package: libpoppler-glib8t64, libsofthsm2
Severity: normal
Hello,
I'm not completely sure where this bug is but there's a bad interaction of Poppler and SoftHSM 2 when used together via the Mozilla NSS backend for PDF signature operations on my Trixie system. This has caused crashes when GNOME Papers is closing as well as crashes when invoking pdfsig, even when I don't pass any arguments, like this:
> $ pdfsig
> pdfsig version 25.03.0
> Copyright 2005-2025 The Poppler Developers - http://poppler.freedesktop.org
> Copyright 1996-2011, 2022 Glyph & Cog, LLC
> Usage: pdfsig [options] <PDF-file> [<output-file>]
> -nssdir <string> : path to directory of libnss3 database
> -nss-pwd <string> : password to access the NSS database (if any)
> -nocert : don't perform certificate validation
> -no-ocsp : don't perform online OCSP certificate revocation check
> -no-appearance : don't add appearance information when signing existing fields
> -aia : use Authority Information Access (AIA) extension for certificate fetching
> -dump : dump all signatures into current directory
> -add-signature : adds a new signature to the document
> -new-signature-field-name <string>: field name used for the newly added signature. A random ID will be used if empty
> -sign <string> : sign the document in the given signature field (by name or number)
> -etsi : create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached
> -backend <string> : use given backend for signing/verification
> -nick <string> : use the certificate with the given nickname/fingerprint for signing
> -kpw <string> : password for the signing key (might be missing if the key isn't password protected)
> -digest <string> : name of the digest algorithm (default: SHA256)
> -reason <string> : reason for signing (default: no reason given)
> -list-nicks : list available nicknames in the NSS database
> -list-backends : print cryptographic signature backends
> -opw <string> : owner password (for encrypted files)
> -upw <string> : user password (for encrypted files)
> -v : print copyright and version info
> -h : print usage information
> -help : print usage information
> --help : print usage information
> -? : print usage information
> Segmentation fault (core dumped)
I haven't witnessed this same weirdness with Okular, but I haven't used it as much for this sort of thing just yet.
Here's how to reproduce in a shell. You will need softhsm2 and libnss3-tools binary packages installed.
1. Change into a clean and empty directory of your choice; perhaps a subdirectory of /tmp/.
2. Craft a SoftHSM configuration file
eval tee $'<<END\ndirectories.tokendir='"$(pwd)"/tokens$'\nobjectstore.backend=file\nEND\n' > softhsm.conf
3. Set SOFTHSM2_CONF environment variable to reference that just-made configuration file
export SOFTHSM2_CONF=$(pwd)/softhsm.conf
4. Initialize SoftHSM; we need not load any actual keys into it.
mkdir ./tokens/
softhsm2-util --init-token --free --label "SoftHSM2 test" --pin 123456 --so-pin 12345678
Now SoftHSM is set up. We need to make a Mozilla NSS database that "knows of" SoftHSM via its PKCS#11 module.
5. Create Mozilla database directory (analogous to ~/.pki/nssdb/ that GNOME applications frequently use)
mkdir ./nssdb/
certutil -N -d ./nssdb/ --empty-password
Note that the database itself has no password, but SoftHSM does, and this is probably an important distinction for further detective work.
6. Add SoftHSM to NSS's known PKCS#11 modules to use
modutil -add "SoftHSM 2" -libfile /usr/lib/softhsm/libsofthsm2.so -dbdir nssdb -force
7. Now you are ready to reproduce. To do that, you can run pdfsig without any arguments except for the one telling it to use our just-made NSS database:
$ pdfsig -nssdir ./nssdb/
Segmentation fault (core dumped)
Reproducing it may require a few tries; it seems like a use-after-free-type issue in an atexit()-registered handler from any one of the several libraries involved. If you're down on your luck, sprinkle in your choice of toppings:
• set LD_PRELOAD=libc_malloc_debug.so GLIBC_TUNABLES=glibc.malloc.check=3
see https://sourceware.org/glibc/manual/latest/html_node/Memory-Allocation-Tunables.html
• disable address space layout randomization using 'setarch' from util-linux as a wrapper, like this:
setarch -vR -- pdfsig -nssdir ./nssdb/
Like many GLib programs, pdfsig uses several threads on every invocation, and especially to print a summary of its command-line options. It seems exit() is called when multiple threads are still around, possibly waiting on condition variables, but only one is left at the time of the crash:
Thread 1 (Thread 0x7ffff5a3f9c0 (LWP 470687) "pdfsig"):
#0 SlotManager::getSlot (this=0x0, slotID=slotID at entry=957537852) at ./src/lib/slot_mgr/SlotManager.cpp:174
#1 0x00007ffff56aaa7d in SoftHSM::C_CloseAllSessions (this=0x55555563a290, slotID=slotID at entry=957537852) at ./src/lib/SoftHSM.cpp:1386
#2 0x00007ffff568ab68 in C_CloseAllSessions (slotID=957537852) at ./src/lib/main.cpp:347
#3 0x00007ffff716a19c in PK11_DestroySlot (slot=0x5555556d7230) at ./nss/lib/pk11wrap/pk11slot.c:454
#4 0x00007ffff716a245 in PK11_FreeSlot (slot=<optimized out>) at ./nss/lib/pk11wrap/pk11slot.c:491
#5 0x00007ffff716ee72 in SECMOD_DestroyModule (module=0x55555563fd60) at ./nss/lib/pk11wrap/pk11util.c:904
#6 SECMOD_DestroyModule (module=0x55555563fd60) at ./nss/lib/pk11wrap/pk11util.c:866
#7 0x00007ffff716f1be in SECMOD_DestroyModuleListElement (element=0x5555556d7210) at ./nss/lib/pk11wrap/pk11util.c:950
#8 0x00007ffff716f6c5 in SECMOD_DestroyModuleList (list=<optimized out>) at ./nss/lib/pk11wrap/pk11util.c:965
#9 0x00007ffff716f74d in SECMOD_Shutdown () at ./nss/lib/pk11wrap/pk11util.c:68
#10 0x00007ffff7121ee9 in nss_Shutdown () at ./nss/lib/nss/nssinit.c:1163
#11 0x00007ffff7121fd8 in NSS_Shutdown () at ./nss/lib/nss/nssinit.c:1221
#12 0x00007ffff7c788fd in shutdownNss () at ./poppler/NSSCryptoSignBackend.cc:215
#13 0x00007ffff744e2b1 in __run_exit_handlers (status=99, listp=0x7ffff75f1680 <__exit_funcs>, run_list_atexit=run_list_atexit at entry=true, run_dtors=run_dtors at entry=true) at ./stdlib/exit.c:118
#14 0x00007ffff744e37a in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:148
#15 0x00007ffff7435caf in __libc_start_call_main (main=main at entry=0x555555557640 <main(int, char**)>, argc=argc at entry=3, argv=argv at entry=0x7fffffffdc58) at ../sysdeps/nptl/libc_start_call_main.h:74
#16 0x00007ffff7435d65 in __libc_start_main_impl (main=0x555555557640 <main(int, char**)>, argc=3, argv=0x7fffffffdc58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc48) at ../csu/libc-start.c:360
#17 0x0000555555559b81 in _start ()
That function body is
/* SlotManager.cpp */
170 // Get one slot
171 Slot* SlotManager::getSlot(CK_SLOT_ID slotID)
172 {
173 try {
174 return slots.at(slotID);
175 } catch( const std::out_of_range &oor) {
176 DEBUG_MSG("slotID is out of range: %s", oor.what());
177 return NULL_PTR;
178 }
179 }
This is about as far as I got. There were some other breadcrumb trails I was following but they've dried up when I switched to this stripped-down reproducer. A common mistake with atexit() handlers occurs in multithreaded programs: when exit handlers are being called, POSIX leaves it unspecified what the state of the other threads are (they may have already been made to terminate, or could still be running autonomously, or something in between). In particular if those other threads are waiting on synchronization objects like condition variables, and an atexit() handler is intended to destroy those synchronization objects, that's big trouble.
-- System Information:
Debian Release: 13.4
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'proposed-updates'), (500, 'stable')
Architecture: amd64 (x86_64)
Kernel: Linux 6.12.85+deb13-amd64 (SMP w/2 CPU threads; PREEMPT)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages libpoppler-glib8t64 depends on:
ii libc6 2.41-12+deb13u2
ii libcairo2 1.18.4-1+b1
ii libfreetype6 2.13.3+dfsg-1+deb13u1
ii libglib2.0-0t64 2.84.4-3~deb13u3
ii libpoppler147 25.03.0-5+deb13u2
ii libstdc++6 14.2.0-19
libpoppler-glib8t64 recommends no packages.
libpoppler-glib8t64 suggests no packages.
Versions of packages libsofthsm2 depends on:
ii libc6 2.41-12+deb13u2
ii libgcc-s1 14.2.0-19
ii libssl3t64 3.5.5-1~deb13u2
ii libstdc++6 14.2.0-19
ii softhsm2-common 2.6.1-3
Versions of packages libsofthsm2 recommends:
ii softhsm2 2.6.1-3
libsofthsm2 suggests no packages.
-- no debconf information
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 411 bytes
Desc: This is a digitally signed message part
URL: <http://alioth-lists.debian.net/pipermail/pkg-freedesktop-maintainers/attachments/20260506/e2b80f87/attachment.sig>
More information about the Pkg-freedesktop-maintainers
mailing list