NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60281: crypto(4): bugs in reference counting and test
>Number: 60281
>Category: kern
>Synopsis: crypto(4): bugs in reference counting and test
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue May 19 05:50:00 +0000 2026
>Originator: Taylor R Campbell
>Release: current
>Organization:
The CryptoBSDev Foundabug, Inc.
>Environment:
>Description:
Several issues found on first pass, but I suspect there's a lot
more I didn't see on this quick skim.
1. Makes no sense to mutex_exit _and then_ acquire a reference;
the object in question may have been freed already between
the mutex_exit and the atomic_inc_uint:
944 mutex_enter(&fcr->lock);
945 TAILQ_FOREACH_SAFE(cse, &fcr->csessions, next, cnext)
946 if (cse->ses == ses) {
947 mutex_exit(&fcr->lock);
948 atomic_inc_uint(&cse->refcnt);
949 return cse;
https://nxr.NetBSD.org/xref/src/sys/opencrypto/cryptodev.c?r=1.128#944
2. For reference count release, atomic_dec must be surrounded
by release/acquire barriers, or else the object might be
destroyed by one thread while another thread is still using
it:
1027 if (atomic_dec_uint_nv(&cse->refcnt) > 0)
1028 return;
1029 mutex_destroy(&cse->lock);
1030 crypto_freesession(cse->sid);
1031 if (cse->key)
1032 free(cse->key, M_XDATA);
1033 if (cse->mackey)
1034 free(cse->mackey, M_XDATA);
1035 pool_put(&csepl, cse);
https://nxr.NetBSD.org/xref/src/sys/opencrypto/cryptodev.c?r=1.128#1027
This must instead be:
membar_release();
if (atomic_dec_uint_nv(&cse->refcnt) > 0)
return;
membar_acquire();
For details, see, e.g.:
https://mail-index.NetBSD.org/source-changes/2023/02/23/msg143446.html
3. Lock order between cryptodev_mtx, struct csession::lock, and
struct fcrypt::lock is not documented.
4. The new test is fundamentally racy and will randomly
spuriously fail depending on who wins the race:
- The t_encrypt thread runs forever issuing
ioctl(CIOCCRYPT) operations:
79 static void *
80 t_encrypt(void *v)
81 {
...
87 for (;;) {
...
96 res = ioctl(a->fd, CIOCCRYPT, &co);
...
103 }
104 }
https://nxr.NetBSD.org/xref/src/tests/crypto/opencrypto/h_thread.c?r=1.1#79
- The main thread starts two t_encrypt threads and then
tries to close the session a second later with
ioctl(CIOCFSESSION), and fails the test if the ioctl
fails:
126 pthread_create(&t, NULL, t_encrypt, &a);
127 b = a;
128 b.msg = '+';
129 pthread_create(&t, NULL, t_encrypt, &b);
130 sleep(1);
131 res = ioctl(a.fd, CIOCFSESSION, &cs.ses);
132 if (res == -1) {
133 warn("CIOCFSESSION");
134 return EXIT_FAILURE;
135 }
https://nxr.NetBSD.org/xref/src/tests/crypto/opencrypto/h_thread.c?r=1.1#126
- ioctl(CIOCFSESSION) fails with EBUSY if another
thread is concurrently using the session:
963 if (atomic_load_relaxed(&cse->refcnt) != 1)
964 return EBUSY;
https://nxr.NetBSD.org/xref/src/sys/opencrypto/cryptodev.c?r=1.128#963
Result: random spurious failures like this:
https://www.NetBSD.org/~martin/sparc64-atf/1023_atf.html#crypto_opencrypto_t_opencrypto_thread
>How-To-Repeat:
code inspection
>Fix:
Yes, please!
Home |
Main Index |
Thread Index |
Old Index